Python __init__.py, más que un archivo vacío
Muchos desarrolladores de Python utilizan el archivo __init__.py como un simple archivo vacío que "hace que este directorio se reconozca como un paquete". Sin embargo, este archivo no es solo un marcador.
La clave de __init__.py es que es "el script que se ejecuta primero cuando se importa el paquete".
Aprovechando esta característica, se puede realizar un trabajo muy poderoso como organizar código desordenado, diseñar claramente la API del paquete y gestionar la lógica de inicialización.
1. Limpieza de rutas de importación desordenadas (simplificación de la API)
Este es el uso más útil y común de __init__.py. Permite 'elevar' clases o funciones de submódulos a nivel de paquete para que el usuario pueda importarlas más fácilmente.
Problema:
Supongamos que el views.py de Django tiene más de 1,000 líneas y se separó en archivos por funcionalidad.
payments/
├── views/
│ ├── __init__.py
│ ├── user_views.py # UserProfileView, ...
│ ├── payment_views.py # PaymentView, ...
│ └── webhooks.py # PaypalWebhookView, ...
└── urls.py
Usar estas vistas en urls.py es muy complicado.
# payments/urls.py (mal ejemplo)
from .views import user_views, payment_views, webhooks
urlpatterns = [
path('profile/', user_views.UserProfileView.as_view()),
path('toss/', payment_views.PaymentView.as_view()),
path('webhook/', webhooks.PaypalWebhookView.as_view()),
]
urls.py debe conocer toda la estructura interna de la carpeta views.
Solución (uso de init.py):
En el archivo views/init.py se importan previamente las vistas necesarias.
# payments/views/__init__.py
# Elevamos las clases/funtions necesarias al nivel del paquete.
from .user_views import UserProfileView
from .payment_views import PaymentView
from .webhooks import PaypalWebhookView
Ahora, el urls.py que utiliza el paquete views parece tener UserProfileView de forma directa.
# payments/urls.py (ejemplo mejorado)
# 'views' ahora es un paquete (directorio) y no un archivo.
from . import views
urlpatterns = [
# Gracias a __init__.py, no necesitamos conocer la estructura interna.
path('profile/', views.UserProfileView.as_view()),
path('toss/', views.PaymentView.as_view()),
path('webhook/', views.PaypalWebhookView.as_view()),
]
El código en urls.py permanece igual, ya sea que views.py sea un archivo o un directorio con varios archivos. Esta es una buena abstracción.
2. Especificación de la 'API oficial' del paquete (__all__)
from my_package import * (importación por comodín) es conveniente, pero puede traer al espacio de nombres variables o módulos no deseados (ej.: import os) que pueden contaminar el código.
__all__ define una lista de permitidos (whitelist) que dice "solo lleva lo que está en esta lista" cuando se utiliza import *.
# payments/views/__init__.py
# Módulos para uso interno
import logging
from .internal_utils import _helper_function
# Vistas a hacer públicas
from .user_views import UserProfileView
from .payment_views import PaymentView
# Definición de __all__:
# "Si se usa import *, solo se pueden obtener UserProfileView y PaymentView."
__all__ = [
'UserProfileView',
'PaymentView'
]
Ahora, incluso si se ejecuta from payments.views import *, el módulo logging o la función interna _helper_function no se importarán, manteniendo así protegido el espacio de nombres.
3. Inicialización del paquete y definición de metadatos
Este es un lugar perfecto para colocar código que debe ejecutarse solo una vez cuando se carga el paquete.
- Proporcionar información de versión:
# my_library/__init__.py
__version__ = "1.2.0"
__author__ = "whitedec"
Ahora los usuarios pueden verificar fácilmente la versión usando import my_library; print(my_library.__version__).
- Ejecutar configuraciones globales:
Un ejemplo típico es que Django llama a django.setup() internamente cuando se importa django, o que el código que inicializa la aplicación de Celery está incluido en init.py.
# my_project/__init__.py (ejemplo de Celery)
# Asegurar que la app de Celery se cargue al iniciar Django
from .celery import app as celery_app
__all__ = ('celery_app',)
※ Nota: Precaución con las referencias circulares entre módulos internos
Hay precauciones que seguir al usar __init__.py como 'mesa de ayuda'.
Supón que views/webhooks.py necesita usar CancelView de views/payment_commons.py.
- Mala práctica (riesgo de referencia circular):
Intentar importar desde webhooks.py, from . import CancelView (es decir, init.py).
- Buena práctica (importación directa):
Importar directamente desde el módulo necesario en webhooks.py, como from .payment_commons import CancelView.
Regla:
- Usuarios externos (por ejemplo,
urls.py): utilizan la API a través de la 'mesa de ayuda' (__init__.py). - Módulos internos (por ejemplo,
webhooks.py): importan funciones directamente del módulo compañero (payment_commons.py) sin pasar por la 'mesa de ayuda'.
Conclusión
__init__.py no es solo un archivo vacío, es una herramienta poderosa que puede desempeñar todos los roles de 'mesa de ayuda', 'documentación oficial de la API', 'script de configuración inicial' de un paquete. Al aprovechar este archivo adecuadamente, se puede hacer que la estructura del proyecto sea mucho más limpia y fácil de mantener.
No hay comentarios.