Organizando programas Python más grandes

PythonPythonBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Si estás escribiendo un programa más grande, realmente no quieres organizarlo como una gran colección de archivos independientes en el nivel superior. Esta sección introduce el concepto de un paquete.

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')
...

Paquetes vs Módulos

Para colecciones más grandes de código, es común organizar los módulos en un paquete.

## De esto
pcost.py
report.py
fileparse.py

## A esto
porty/
    __init__.py
    pcost.py
    report.py
    fileparse.py

Elegís un nombre y creáis un directorio de nivel superior. porty en el ejemplo anterior (claramente, elegir este nombre es el primer paso más importante).

Agregá un archivo __init__.py al directorio. Puede estar vacío.

Colocá tus archivos fuente en el directorio.

Usando un Paquete

Un paquete sirve como un espacio de nombres para las importaciones.

Esto significa que ahora hay importaciones de múltiples niveles.

import porty.report
port = porty.report.read_portfolio('portfolio.csv')

Hay otras variaciones de las 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

Este enfoque tiene dos problemas principales.

  • Las importaciones entre archivos en el mismo paquete se rompen.
  • Los scripts principales ubicados dentro del paquete se rompen.

Entonces, básicamente todo se rompe. Pero, aparte de eso, funciona.

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 modificada.

from porty import fileparse

def read_portfolio(filename):
    return fileparse.parse_csv(...)

Todas las importaciones son absolutas, no relativas.

import fileparse    ## SE ROMPE. fileparse no se encuentra

...

Importaciones Relativas

En lugar de usar directamente el nombre del paquete, puedes usar . para referirte al paquete actual.

from. import fileparse

def read_portfolio(filename):
    return fileparse.parse_csv(...)

Sintaxis:

from. import modname

Esto facilita renombrar el paquete.

Problema: Scripts Principales

Ejecutar un submódulo de un paquete como un script principal se rompe.

$ python porty/pcost.py ## SE ROMPE
...

Razón: Estás ejecutando Python en un solo archivo y Python no ve correctamente el resto de la estructura del paquete (sys.path está incorrecto).

Todas las importaciones se rompen. Para solucionar esto, debes ejecutar tu programa de una manera diferente, usando la opción -m.

$ python -m porty.pcost ## FUNCIONA
...

Archivos __init__.py

El propósito principal de estos archivos es unir los módulos.

Ejemplo: consolidando 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 usar importaciones de múltiples niveles.

from porty import pcost
pcost.portfolio_cost('portfolio.csv')

Otra solución para los scripts

Como se ha señalado, ahora debes usar -m package.module para ejecutar scripts dentro de tu paquete.

$ python3 -m porty.pcost portfolio.csv

Hay otra alternativa: Escribe un nuevo script de nivel superior.

#!/usr/bin/env python3
## pcost.py
import porty.pcost
import sys
porty.pcost.main(sys.argv)

Este script se encuentra fuera del paquete. Por ejemplo, mirando la estructura de directorios:

pcost.py       ## script de nivel superior
porty/         ## directorio del paquete
    __init__.py
    pcost.py
 ...

Estructura de la Aplicación

La organización del código y la estructura de archivos son clave para la mantenibilidad de una aplicación.

No existe un enfoque "uno para todos" para Python. Sin embargo, una estructura que funciona para muchos problemas es la siguiente.

porty-app/
  README.txt
  script.py         ## SCRIPT
  porty/
    ## CÓDIGO DE LIBRERÍA
    __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.

Una vez más, los scripts de nivel superior (si los hay) deben existir fuera del paquete de código. Un nivel arriba.

#!/usr/bin/env python3
## porty-app/script.py
import sys
import porty

porty.report.main(sys.argv)

En este momento, tienes un directorio con varios programas:

pcost.py          ## Calcula el costo de la cartera
report.py         ## Genera un informe
ticker.py         ## Produce un cotizador de acciones en tiempo real

Hay una variedad de módulos de soporte con otras funcionalidades:

stock.py          ## Clase Stock
portfolio.py      ## Clase Portfolio
fileparse.py      ## Análisis de CSV
tableformat.py    ## Tablas formateadas
follow.py         ## Sigue un archivo de registro
typedproperty.py  ## Propiedades de clase tipadas

En este ejercicio, vamos a limpiar el código y ponerlo en un paquete común.

Ejercicio 9.1: Crear un paquete simple

Crea un directorio llamado porty/ y coloca todos los archivos de Python anteriores en él. Además, crea un archivo __init__.py vacío y colócalo en el directorio. Deberías 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 se encuentra en tu directorio. Este contiene módulos de Python pre-compilados de antes. Queremos comenzar de nuevo.

Intenta importar algunos de los módulos del paquete:

>>> import porty.report
>>> import porty.pcost
>>> import porty.ticker

Si estas importaciones fallan, ve al archivo apropiado y corrige las importaciones de módulos para incluir una importación relativa al paquete. Por ejemplo, una declaración como import fileparse podría cambiar a la siguiente:

## report.py
from. import fileparse

...

Si tienes una declaración como from fileparse import parse_csv, cambia el código a lo siguiente:

## report.py
from.fileparse import parse_csv

...

✨ Revisar Solución y Practicar

Ejercicio 9.2: Crear un directorio de aplicación

Poner todo tu código en un "paquete" no es a menudo suficiente para una aplicación. A veces hay archivos de soporte, documentación, scripts y otras cosas. Estos archivos deben existir FUERA del directorio porty/ que creaste anteriormente.

Crea un nuevo directorio llamado porty-app. Mueve el directorio porty que creaste en el Ejercicio 9.1 a ese directorio. Copia los archivos de prueba portfolio.csv y prices.csv en este directorio. Además, crea un archivo README.txt con información sobre ti mismo. Tu código ahora debería estar organizado como sigue:

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 tu código, debes asegurarte de trabajar en el directorio principal porty-app/. Por ejemplo, desde la terminal:

$ cd porty-app
$ python3
>>> import porty.report
>>>

Intenta ejecutar algunos de tus scripts anteriores como un programa principal:

$ cd porty-app
$ 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

$

Ejercicio 9.3: Scripts de nivel superior

Usar el comando python -m a menudo es un poco extraño. Es posible que desees escribir un script de nivel superior que simplemente gestione las particularidades de los paquetes. Crea un script print-report.py que genere el informe anterior:

#!/usr/bin/env python3
## print-report.py
import sys
from porty.report import main
main(sys.argv)

Coloca este script en el directorio principal porty-app/. Asegúrate de que puedas ejecutarlo en esa ubicación:

$ cd porty-app
$ 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

$

Tu código final ahora debería estar estructurado de algo así:

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
✨ Revisar Solución y Practicar

Resumen

¡Felicidades! Has completado el laboratorio de Paquetes. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.