Python __init__.py, больше чем просто пустой файл
Многие разработчики Python используют файл __init__.py как пустой файл для «распознавания этой директории как пакета». Однако этот файл не просто маркер.
Суть __init__.py заключается в том, что это «скрипт, который выполняется первым при импортировании пакета».
Используя эту особенность, можно выполнить очень мощные задачи, такие как очистка неаккуратного кода, ясное проектирование API пакета и управление логикой инициализации.
1. Упрощение неаккуратных импортов (Упрощение API)
Это самый полезный и общий способ использования __init__.py. Он «поднимает» классы или функции подпакетов на уровень пакета, чтобы пользователи могли легче их импортировать.
Проблема:
Предположим, что views.py в Django содержит больше 1000 строк, и файлы разделены по функциям.
payments/
├── views/
│ ├── __init__.py
│ ├── user_views.py # UserProfileView и др.
│ ├── payment_views.py # PaymentView и др.
│ └── webhooks.py # PaypalWebhookView и др.
└── urls.py
Использовать эти виды в urls.py может быть очень хлопотно.
# payments/urls.py (плохой пример)
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 должен знать всю внутреннюю структуру views.
Решение (Использование init.py):
Импортируем необходимые виды заранее в файл views/init.py.
# payments/views/__init__.py
# Поднимаем необходимые классы/функции на уровень пакета.
from .user_views import UserProfileView
from .payment_views import PaymentView
from .webhooks import PaypalWebhookView
Теперь urls.py, использующий пакет views, выглядит так, будто views непосредственно содержит UserProfileView.
# payments/urls.py (улучшенный пример)
# 'views' теперь это пакет (директория), а не файл.
from . import views
urlpatterns = [
# Благодаря __init__.py теперь не нужно знать внутреннюю структуру.
path('profile/', views.UserProfileView.as_view()),
path('toss/', views.PaymentView.as_view()),
path('webhook/', views.PaypalWebhookView.as_view()),
]
Код urls.py остается неизменным, независимо от того, является ли views.py файлом или директорией с несколькими файлами. Это хорошая абстракция.
2. Явное указание 'официального API' пакета (__all__)
from my_package import * (импорт с подстановочным знаком) удобен, но может привести к тому, что непреднамеренные переменные или модули (например, import os) попадут в текущее пространство имен и загрязнят код.
__all__ определяет разрешенный список, указывая, «брали только то, что есть в этом списке», когда используется import *.
# payments/views/__init__.py
# Модули для внутреннего использования
import logging
from .internal_utils import _helper_function
# Открытые виды
from .user_views import UserProfileView
from .payment_views import PaymentView
# Определение __all__:
# "при использовании import * можно будет взять только UserProfileView и PaymentView."
__all__ = [
'UserProfileView',
'PaymentView'
]
Теперь, если выполнить from payments.views import *, модуль logging или внутренняя функция _helper_function не будут импортированы, что позволяет безопасно защитить пространство имен.
3. Инициализация пакета и определение метаданных
Это идеальное место для размещения кода, который должен выполняться всего один раз при загрузке пакета.
- Предоставление информации о версии:
# my_library/__init__.py
__version__ = "1.2.0"
__author__ = "whitedec"
Теперь пользователь может легко проверить версию, выполнив import my_library; print(my_library.__version__).
- Выполнение глобальных настроек:
Представьте, что Django вызывает django.setup() при импортировании django, или что код, инициализирующий Celery, включен в init.py. Это типичный пример таких случаев.
# my_project/__init__.py (пример с Celery)
# Обеспечение загрузки Celery вместе с Django
from .celery import app as celery_app
__all__ = ('celery_app',)
※ Предостережение: Циклические ссылки между внутренними модулями
Существуют некоторые вещи, на которые следует обратить внимание, когда вы используете __init__.py как «информационный стол».
Предположим, что views/webhooks.py должен использовать CancelView из views/payment_commons.py.
- Плохой способ (риск циклической ссылки):
Попытка импортировать CancelView через from . import CancelView из webhooks.py (т.е. через init.py).
- Хороший способ (прямой импорт):
Импортировать непосредственно из необходимых модулей, например from .payment_commons import CancelView в webhooks.py.
Правило:
- Внешние пользователи (например,
urls.py): используют API через 'информационный стол' (__init__.py). - Внутренние модули (например,
webhooks.py): импортируют функционал напрямую из соседних модулей (payment_commons.py) без прохода через 'информационный стол'.
Заключение
__init__.py — это не просто пустой файл, это мощный инструмент, выполняющий роль 'информационного стола', 'официальной документации API' и 'скрипта начальной настройки' пакета. При правильном использовании этого файла можно сделать структуру проекта намного более аккуратной и удобной для поддержки.
Комментариев нет.