De un archivo a una carpeta de módulos
Un solo archivo deja de bastar cuando el proyecto crece. Un paquete es una carpeta de módulos que Python puede importar como una unidad. Marca la carpeta con un archivo llamado __init__.py (puede estar vacío) y cada archivo .py de dentro pasa a ser un módulo del paquete, nombrado con puntos: quant/pricing.py es quant.pricing, y quant/data/loaders.py es quant.data.loaders. Las carpetas dentro de un paquete son subpaquetes y necesitan su propio __init__.py.
# A small project on disk
#
# quant-tools/
# pyproject.toml
# README.md
# quant/
# __init__.py
# pricing.py
# data/
# __init__.py
# loaders.py
# tests/
# test_pricing.py
# quant/pricing.py
def mid(bid, ask):
return (bid + ask) / 2
# anywhere else in the project
from quant.pricing import mid
import quant.data.loaders
print(mid(184.0, 185.0)) # 184.5Una estructura que escala
Mantén el código importable en una carpeta de paquete con el nombre del proyecto y todo lo demás al lado, no dentro: las pruebas en tests/, un README.md que explique el proyecto, un pyproject.toml que lo nombre y liste sus dependencias, y los datos o notebooks en sus propias carpetas. Los archivos de prueba se llaman test_*.py para que herramientas como pytest los encuentren. Módulos pequeños y enfocados con nombres claros (pricing.py, loaders.py) son más fáciles de probar y reutilizar que un utils.py larguísimo.
# pyproject.toml (names the project and its dependencies)
#
# [project]
# name = "quant-tools"
# version = "0.1.0"
# requires-python = ">=3.11"
# dependencies = ["numpy"]
# tests/test_pricing.py: found automatically because of the name
from quant.pricing import mid
def test_mid():
assert mid(184.0, 185.0) == 184.5Importar dentro de un paquete
Dentro del paquete puedes importar por ruta completa desde arriba (from quant.pricing import mid, importación absoluta) o relativa al módulo actual (from .pricing import mid, un punto para el mismo paquete, dos para su padre, importación relativa). Las absolutas suelen ser más claras. __init__.py se ejecuta al importar el paquete por primera vez, así que puede reexportar los nombres que los usuarios necesitan (from .pricing import mid) y entonces import quant; quant.mid(...) funciona. Ejecuta el código de un paquete con python -m quant.data.loaders, no con python quant/data/loaders.py, para que los nombres con puntos se resuelvan.
# quant/__init__.py: runs on the first "import quant"
from .pricing import mid # relative: same package
__all__ = ["mid"] # what "from quant import *" would give
# quant/data/loaders.py
from quant.pricing import mid # absolute: always starts at the top
from .. import pricing # relative: two dots = the parent package
# In a terminal, from the project folder:
# python -m quant.data.loaders (runs the module as part of the package)
# pytest -q (finds tests/test_*.py)# A file path and its module name are two spellings of one thing
path = "quant/data/loaders.py"
name = path[:-3].replace("/", ".")
print(name) # quant.data.loaders
print(name.split(".")[0]) # quant (the top-level package)
# __init__.py stands for its folder
print("quant/__init__.py"[:-3].replace("/", ".")) # quant.__init__ -> the package is "quant"__init__.py (un «paquete de espacio de nombres»), pero las herramientas, los ejecutores de pruebas y los instaladores se comportan de forma incoherente, y importaciones que funcionaban en tu equipo fallan en otro. Da un __init__.py a cada carpeta de paquete. El lab te pide encontrar las carpetas que contienen código pero carecen de uno.- Un paquete es una carpeta con
__init__.py;quant/data/loaders.pyes el móduloquant.data.loaders. - Mantén el código en el paquete; pruebas, README,
pyproject.tomly datos al lado. - Prefiere importaciones absolutas; ejecuta el código de un paquete con
python -m package.module.
A continuación: entornos virtuales y pip, para que cada proyecto tenga exactamente las bibliotecas que necesita.
Lab: qué se califica
- Convertir una ruta de archivo en nombre de módulo con
module_name25 pts - Listar los módulos importables de un proyecto con
find_modules25 pts - Detectar las carpetas sin __init__.py con
missing_init25 pts - Escribir la sentencia de importación de un módulo con
import_line25 pts