Paquetes
Concluiremos el curso con algunos detalles para la organización de código en paquetes. Además, discutiremos sobre la instalación de paquetes de terceros y sobre la preparación necesaria para entregarle nuestro código a otros. El tema de empaquetamiento de código essta en constante evolución y es una parte compleja en el desarrollo de Python. Por tanto, esta sección esta enfocada en compartir algunos principios de organización general de código.
9.1 Paquetes
Si está escribiendo un programa más grande, realmente no desea organizarlo como una gran colección de archivos independientes en el nivel superior. Esta sección presenta el concepto de paquete.
9.1.1 Módulos
Cualquier archivo fuente de Python es un módulo.
# foo.py
def grok(a):
...
def spam(b):
...
Una declaración import carga y ejecuta un módulo.
# program.py
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
9.1.2 Paquetes vs Módulos
Para colecciones más grandes de código, es común organizar los módulos en un paquete.
# From this
pcost.py
report.py
fileparse.py
# To this
porty/
__init__.py
pcost.py
report.py
fileparse.py
Elige un nombre y crea un directorio de nivel superior. porty
en el ejemplo anterior (claramente elegir este nombre es el primer paso más importante).
Agregue un archivo __init__.py
al directorio. Puede estar vacío.
Coloque sus archivos de origen en el directorio.
9.1.3 Usando un paquete
Un paquete sirve como espacio de nombres para las importaciones.
Esto significa que ahora hay importaciones multinivel.
import porty.report
port = porty.report.read_portfolio('port.csv')
Hay otras variaciones de declaraciones de importación.
from porty import report
port = report.read_portfolio('portfolio.csv')
from porty.report import read_portfolio
port = read_portfolio('portfolio.csv')
Dos problemas
Hay dos problemas principales con este enfoque.
- importa entre archivos en el mismo paquete.
- Scripts principales colocados dentro del paquete.
Entonces, básicamente todo se rompe. Pero, aparte de eso, funciona.
9.1.4 Problema: Importaciones
Las importaciones entre archivos en el mismo paquete ahora deben incluir el nombre del paquete en la importación . Recuerda la estructura.
porty/
__init__.py
pcost.py
report.py
fileparse.py
Ejemplo de importación modificado.
# report.py
from porty import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
Todas las importaciones son absolutas, no relativas.
# report.py
import fileparse # Se ROMPE. fileparse not found
...
9.1.5 Importaciones relativas
En lugar de usar directamente el nombre del paquete, puede usar .
para hacer referencia al paquete actual.
# report.py
from . import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
Sintaxis:
from . import modname
Esto facilita el cambio de nombre del paquete.
9.1.6 Problema: guiones principales
La ejecución de un submódulo de paquete como un script principal se rompe.
bash $ python porty/pcost.py # BREAKS
...
Motivo: está ejecutando Python en un solo archivo y Python no ve el resto de la estructura del paquete correctamente (sys.path
es incorrecto).
Todas las importaciones se rompen. Para solucionar este problema, debe ejecutar su programa de una manera diferente, usando la opción -m
.
bash $ python -m porty.pcost # WORKS
...
9.1.7 archivos __init__.py
El propósito principal de estos archivos es unir módulos.
Ejemplo: consolidar funciones
# porty/__init__.py
from .pcost import portfolio_cost
from .report import portfolio_report
Esto hace que los nombres aparezcan en el nivel superior al importar.
from porty import portfolio_cost
portfolio_cost('portfolio.csv')
En lugar de utilizar las importaciones multinivel.
from porty import pcost
pcost.portfolio_cost('portfolio.csv')
9.1.8 Otra solución para scripts
Como se señaló, ahora debe usar -m paqute.modulo
para ejecutar scripts dentro de su paquete.
bash % python3 -m porty.pcost portfolio.csv
Hay otra alternativa: escriba un nuevo script de nivel superior.
#!/usr/bin/env python3
# pcost.py
import porty.pcost
import sys
porty.pcost.main(sys.argv)
Este script vive fuera del paquete. Por ejemplo, mirando la estructura del directorio:
pcost.py # guion de nviel superior
porty/ # directorio del paquete
__init__.py
pcost.py
...
9.1.9 Estructura de la aplicación
La organización del código y la estructura de archivos es clave para el mantenimiento de una aplicación.
No existe un enfoque de "talla única" para Python. Sin embargo, una estructura que funciona para muchos problemas es algo como esto.
porty-app/
README.txt
script.py # SCRIPT
porty/
# codigo de la biblioteca / libreria
__init__.py
pcost.py
report.py
fileparse.py
El nivel superior porty-app
es un contenedor para todo lo demás: documentación, scripts de nivel superior, ejemplos, etc.
Nuevamente, los scripts de nivel superior (si los hay) deben existir fuera del paquete de código. Un nivel más.
#!/usr/bin/env python3
# porty-app/script.py
import sys
import porty
porty.report.main(sys.argv)
9.1.10 Ejercicios
En este punto, tiene un directorio con varios programas:
pcost.py # computes portfolio cost
report.py # Makes a report
ticker.py # Produce a real-time stock ticker
Hay una variedad de módulos de soporte con otras funcionalidades:
stock.py # Stock class
portfolio.py # Portfolio class
fileparse.py # CSV parsing
tableformat.py # Formatted tables
follow.py # Follow a log file
typedproperty.py # Typed class properties
En este ejercicio, vamos a limpiar el código y ponerlo en un paquete común.
Ejercicio 9.1: Hacer un paquete simple
Cree un directorio llamado porty/y coloque todos los archivos de Python anteriores en él. Además, cree un init.pyarchivo vacío y colóquelo en el directorio. Debería tener un directorio de archivos como este:
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
Elimina el archivo __pycache__
que está en tu directorio. Este contiene módulos de Python precompilados de antes. Queremos empezar de nuevo.
Intente importar algunos de los módulos del paquete:
>>> import porty.report
>>> import porty.pcost
>>> import porty.ticker
Si estas importaciones fallan, vaya al archivo apropiado y corrija las importaciones del módulo para incluir una importación relativa al paquete. Por ejemplo, una declaración como import fileparse
podría cambiar a lo siguiente:
# report.py
from . import fileparse
...
Si tiene una declaración como from fileparse import parse_csv, cambie el código a lo siguiente:
# report.py
from .fileparse import parse_csv
...
Ejercicio 9.2: Crear un directorio de aplicaciones
Poner todo su código en un "paquete" no suele ser suficiente para una aplicación. A veces hay archivos de apoyo, documentación, scripts y otras cosas. Estos archivos deben existir FUERA del directorio porty/
que creó anteriormente.
Cree un nuevo directorio llamado porty-app. Mueva el portydirectorio que creó en el ejercicio 9.1 a ese directorio. Copie los archivos de prueba Data/portfolio.csv
y Data/prices.csv
en este directorio. Además, cree un archivo README.txt
con información sobre usted. Su código ahora debería estar organizado de la siguiente manera:
porty-app/
portfolio.csv
prices.csv
README.txt
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
Para ejecutar su código, debe asegurarse de estar trabajando en el directorio porty-app/
de nivel superior . Por ejemplo, desde la terminal:
shell % cd porty-app
shell % python3
>>> import porty.report
>>>
Intente ejecutar algunos de sus scripts anteriores como programa principal:
shell % cd porty-app
shell % python3 -m porty.report portfolio.csv prices.csv txt
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
shell %
Ejercicio 9.3: Scripts de nivel superior
Usar el comando python -m
suele ser un poco extraño. Es posible que desee escribir un script de nivel superior que simplemente se ocupe de las rarezas de los paquetes. Cree un script print-report.py
que produzca el informe anterior:
#!/usr/bin/env python3
# print-report.py
import sys
from porty.report import main
main(sys.argv)
Coloque este script en el directorio porty-app/
de nivel superior. Asegúrese de poder ejecutarlo en esa ubicación:
shell % cd porty-app
shell % python3 print-report.py portfolio.csv prices.csv txt
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
shell %
Su código final ahora debería estar estructurado de esta manera:
porty-app/
portfolio.csv
prices.csv
print-report.py
README.txt
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
9.2 Módulos de terceros
Python tiene una gran biblioteca de módulos integrados (baterías incluidas).
Incluso hay más módulos de terceros. Compruébelos en el índice de paquetes de Python o PyPi. O simplemente haga una búsqueda en Google de un tema específico.
Cómo manejar las dependencias de terceros es un tema en constante evolución con Python. Esta sección simplemente cubre los conceptos básicos para ayudarlo a comprender cómo funciona.
9.2.1 La ruta de búsqueda del módulo
sys.path
es un directorio que contiene la lista de todos los directorios controlados por la declaración import
. Míralo:
>>> import sys
>>> sys.path
... look at the result ...
>>>
Si importa algo y no está ubicado en uno de esos directorios, obtendrá una excepción ImportError
.
9.2.2 Módulos de biblioteca estándar
Los módulos de la biblioteca estándar de Python generalmente provienen de una ubicación como /usr/local/lib/python3.6
. Puede averiguarlo con certeza haciendo una prueba breve:
>>> import re
>>> re
<module 're' from '/usr/local/lib/python3.6/re.py'>
>>>
Simplemente mirar un módulo en el REPL es un buen consejo de depuración que debe conocer. Le mostrará la ubicación del archivo.
9.2.3 Módulos de terceros
Los módulos de terceros generalmente se encuentran en un directorio dedicado site-packages
. Lo verá si realiza los mismos pasos que el anterior:
>>> import numpy
>>> numpy
<module 'numpy' from '/usr/local/lib/python3.6/site-packages/numpy/__init__.py'>
>>>
Nuevamente, mirar un módulo es un buen consejo de depuración si está tratando de averiguar por qué algo relacionado con import
no está funcionando como se esperaba.
9.2.4 Instalación de módulos
La técnica más común para instalar un módulo de terceros es usar pip
. Por ejemplo:
bash % python3 -m pip install packagename
Este comando descargará el paquete y lo instalará en el directorio site-packages
.
9.2.5 Problemas
- Es posible que esté utilizando una instalación de Python que no controla directamente.
- Una instalación aprobada por la empresa
- Estás usando la versión de Python que viene con el sistema operativo.
- Es posible que no tenga permiso para instalar paquetes globales en la computadora.
- Puede haber otras dependencias.
9.2.6 Ambientes virtuales
Una solución común para los problemas de instalación de paquetes es crear un "entorno virtual" para usted. Naturalmente, no hay "una manera" de hacer esto; de hecho, existen varias herramientas y técnicas en competencia. Sin embargo, si está utilizando una instalación estándar de Python, puede intentar escribir esto:
bash % python -m venv mypython
bash %
Después de unos momentos de espera, tendrá un nuevo directorio mypython
que es su propia instalación de Python. Dentro de ese directorio encontrará un directorio bin/
(Unix) o un directorio Scripts/
(Windows). Si ejecuta el script activate
que se encuentra allí, “activará” esta versión de Python, convirtiéndola en el comando python
predeterminado para el shell. Por ejemplo:
bash % source mypython/bin/activate
(mypython) bash %
Desde aquí, ahora puede comenzar a instalar paquetes de Python usted mismo. Por ejemplo:
(mypython) bash % python -m pip install pandas
...
Con el fin de experimentar y probar diferentes paquetes, un entorno virtual normalmente funcionará bien. Si, por otro lado, está creando una aplicación y tiene dependencias de paquetes específicas, ese es un problema ligeramente diferente.
9.2.7 Manejo de dependencias de terceros en su aplicación
Si ha escrito una aplicación y tiene dependencias específicas de terceros, un desafío se refiere a la creación y preservación del entorno que incluye su código y las dependencias. Lamentablemente, esta ha sido un área de gran confusión y cambios frecuentes durante la vida de Python. Continúa evolucionando incluso ahora.
En lugar de proporcionar información que seguramente estará desactualizada pronto, le recomiendo la Guía del usuario de Python Packaging.
9.2.8 Ejercicios
Ejercicio 9.4: Creación de un entorno virtual
Vea si puede recrear los pasos para crear un entorno virtual e instalar pandas en él como se muestra arriba.
9.3 Distribución
En algún momento, es posible que desee dar su código a otra persona, posiblemente solo a un compañero de trabajo. Esta sección brinda la técnica más básica para hacerlo. Para obtener información más detallada, deberá consultar la Guía del usuario de empaquetado de Python.
9.3.1 Creando un archivo setup.py
Agregue un archivo setup.py
al nivel superior del directorio de su proyecto.
# setup.py
import setuptools
setuptools.setup(
name="porty",
version="0.0.1",
author="Your Name",
author_email="you@example.com",
description="Practical Python Code",
packages=setuptools.find_packages(),
)
9.3.2 Creando MANIFEST.in
Si hay archivos adicionales asociados con su proyecto, especifíquelos con un MANIFEST.inarchivo. Por ejemplo:
# MANIFEST.in
include *.csv
Coloque el archivo MANIFEST.in
en el mismo directorio que setup.py
.
9.3.2 Crear una distribución de origen
Para crear una distribución de su código, use el archivo setup.py
. Por ejemplo:
bash % python setup.py sdist
Esto creará un archivo .tar.gzo.zip
en el directorio dist/
. Ese archivo es algo que ahora puede regalar a otros.
9.3.3 Instalando su código
Otros pueden instalar su código Python usando pi
p de la misma manera que lo hacen con otros paquetes. Simplemente deben proporcionar el archivo creado en el paso anterior. Por ejemplo:
bash % python -m pip install porty-0.0.1.tar.gz
Comentario
Los pasos anteriores describen los conceptos básicos más mínimos para crear un paquete de código Python que puede darle a otra persona. En realidad, puede ser mucho más complicado dependiendo de las dependencias de terceros, si su aplicación incluye o no código externo (es decir, C / C ++), etc. Cubrir eso está fuera del alcance de este curso. Solo hemos dado un pequeño primer paso.
9.3.4 Ejercicios
Ejercicio 9.5: hacer un paquete
Tome el código porty-app/
que creó para el ejercicio 9.3 y vea si puede recrear los pasos descritos aquí. Específicamente, agregue un archivo setup.py
y un archivo MANIFEST.in
al directorio de nivel superior. Cree un archivo de distribución de origen ejecutando python setup.py sdist
.
Como paso final, vea si puede instalar su paquete en un entorno virtual de Python.