Módulos, Importaciones y Paquetes
Python organiza el código en módulos (archivos) y paquetes (directorios). El sistema de importación carga el código una vez por proceso, lo almacena en caché en sys.modules y expone los nombres a través del espacio de nombres de importación.
Receta
# myapp/services/users.py
from myapp.models.user import User
from myapp.utils.text import slugify
def list_active() -> list[User]:
return [u for u in User.all() if u.active]Cuándo usar esto:
- Para dividir un script en crecimiento en paquetes mantenibles
- Para publicar bibliotecas reutilizables en PyPI
- Para ejecutar puntos de entrada de CLI a través de
python -m - Para cargar perezosamente dependencias opcionales pesadas
Ejemplo de Trabajo
# myapp/__init__.py
__version__ = "1.0.0"
# myapp/config.py
from dataclasses import dataclass
import os
@dataclass(frozen=True)
class Settings:
debug: bool
database_url: str
def load_settings() -> Settings:
return Settings(
debug=os.getenv("DEBUG", "0") == "1",
database_url=os.getenv("DATABASE_URL", "sqlite:///local.db"),
)
# myapp/main.py
from myapp.config import load_settings
def main() -> None:
settings = load_settings()
print(f"debug={settings.debug} db={settings.database_url}")
if __name__ == "__main__":
main()Ejecuta con: python -m myapp.main desde el directorio padre en PYTHONPATH.
Lo que esto demuestra:
- El archivo
__init__.pydel paquete puede exponer metadatos de versión - Las importaciones absolutas (
from myapp.config import ...) permanecen claras a medida que el paquete crece - Guardia de entrada
if __name__ == "__main__"para módulos ejecutables - El modificador
-mestablece__package__correctamente para importaciones relativas dentro del paquete
Profundización
Cómo Funciona
- Ruta de búsqueda de módulos -
sys.pathlista directorios;PYTHONPATHy lossite-packagesde venv lo extienden. - Caché de módulos - La primera importación ejecuta el cuerpo del módulo; las importaciones posteriores reutilizan
sys.modules. - Importaciones relativas -
from .config import load_settingsrequieren contexto de paquete (-mo importación de paquete). - Paquetes de espacio de nombres - Los directorios PEP 420 sin
__init__.pypueden participar en un espacio de nombres dividido. __all__- Documenta los nombres públicos parafrom module import *(raro en el código de la aplicación).
Recomendación de Diseño
project/
src/
myapp/
__init__.py
main.py
config.py
pyproject.toml
Instala en modo editable (uv pip install -e .) para que import myapp se resuelva durante el desarrollo.
Notas de Python
import importlib
def lazy_import(module: str):
return importlib.import_module(module)
# El bloque TYPE_CHECKING evita importaciones circulares en tiempo de ejecución
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from myapp.models import UserTrampas Comunes
- Importaciones circulares -
aimportabmientrasbimportaa. Solución: Mueve tipos compartidos a un tercer módulo o usa importación perezosa dentro de funciones. - Ejecutar archivo directamente -
python myapp/main.pyrompe las importaciones relativas. Solución:python -m myapp.main. - Sobrescribir la biblioteca estándar - Nombrar un archivo
json.pyrompeimport json. Solución: Evita nombres de la biblioteca estándar para módulos. - Efectos secundarios en la importación - Las conexiones de base de datos a nivel de módulo ralentizan las pruebas. Solución: Funciones de fábrica llamadas desde
main. - Importaciones de estrella -
from utils import *contamina el espacio de nombres. Solución: Importa símbolos explícitos.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Script de módulo único | <200 líneas, un autor | Múltiples dominios emergentes |
Paquete con diseño src | Bibliotecas y servicios | Notebook desechable |
importlib perezoso | Dependencias opcionales pesadas (torch) | Dependencia principal siempre necesaria |
Puntos de entrada pluggy | Arquitecturas de plugins | Paquete interno simple |
Preguntas Frecuentes
¿Cuál es la diferencia entre import y from import?
import module enlaza el objeto del módulo. from module import name enlaza el nombre directamente en el espacio de nombres actual.
¿Necesito __init__.py?
Los paquetes regulares sí (o un paquete de espacio de nombres implícito sin él). Un __init__.py vacío está bien para marcar un paquete.
¿Cómo ayuda python -m?
Ejecuta un módulo como __main__ con metadatos de paquete correctos para que las importaciones relativas funcionen.
¿Qué es sys.path[0]?
El directorio que contiene el script o el directorio actual para -m. Las instalaciones editables añaden src a la ruta a través de metadatos.
¿Cómo soluciono ImportError después de un refactor?
Verifica el paquete en PYTHONPATH, usa importaciones absolutas, verifica el descubrimiento de paquetes en pyproject.toml, reinstala en modo editable.
¿Puedo importar desde el directorio padre?
Posible con trucos de ruta - desaconsejado. Instala el paquete en modo editable o configura un diseño adecuado en su lugar.
¿Qué hace if TYPE_CHECKING?
Importa solo para verificadores de tipos - evita importaciones circulares en tiempo de ejecución mientras se conservan las sugerencias.
¿Cómo funcionan las importaciones relativas?
Un punto es el paquete actual; dos puntos es el padre. Requiere contexto de paquete - no en scripts de nivel superior.
¿Cuándo es aceptable import *?
Raramente - a veces en __init__.py para reexportar APIs con __all__ explícito. Nunca en módulos de aplicación.
¿Cómo difieren los paquetes de espacio de nombres?
Múltiples directorios contribuyen a un solo nombre de paquete sin un __init__.py único - se usa para ecosistemas de plugins.
Relacionado
- Inicio Rápido de Entornos Virtuales - dependencias aisladas
- Diseño
src- diseño de paquetes de producción - Explicación de
pyproject.toml- metadatos del paquete - Archivos y E/S - lectura de archivos de configuración
Versiones de la Pila: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.