Файлы
__init__.py
стали необязательны с Python 3.3, но их лучше все равно использовать.
Если файл с именем __init__.py
присутствует в каталоге пакета, то он вызывается при импорте пакета или модуля в пакете. Это может быть использовано для выполнения кода инициализации пакета, например инициализации данных уровня пакета.
Поместим следующий код в файл __init__.py
:
print(f'Файл __init__.py в пакете {__name__}') var_init = ['one', 'two', 'three']
Добавим файл __init__.py
в каталог pkg
из примера "Пакеты модулей в Python":
-- pkg | |---- __init__.py | |---- mod1.py | |---- mod2.py
Теперь при импорте пакета инициализируется глобальная переменная var_init
:
>>> import pkg # Файл __init__.py в пакете pkg >>> pkg.var_init # ['one', 'two', 'three']
Модуль в пакете может получить доступ к глобальным переменным пакета файла __init__.py
, импортируя его в свою очередь. Изменим файл модуля пакета mod1.py
следующим образом:
mod1.py
:
def foo(): from pkg import var_init print('Модуль 1, функция foo(), var_init =', var_init)
>>> from pkg import mod1 # Вызов __init__.py в пакете pkg >>> mod1.foo() # Модуль 1, функция foo(), var_init = ['one', 'two', 'three']
Файл __init__.py
может также использоваться для автоматического импорта модулей пакета. В материале "Пакеты модулей в Python" говорилось, что оператор import pkg
помещает только имя пакета pkg
в область видимости вызывающего объекта и не импортирует никакие модули. Изменим файл пакета __init__.py
в каталоге pkg
, дописав строку импорта модулей import pkg.mod1, pkg.mod2
.
__init__.py
:
print(f'Файл __init__.py в пакете {__name__}') x = ['one', 'two', 'three'] import pkg.mod1, pkg.mod2
Теперь при импорте пакета pkg
, модули в mod1
и mod2
импортируются автоматически:
>>> import pkg # Файл __init__.py в пакете pkg >>> pkg.mod1.foo() # Модуль 1, функция foo(), var_init = ['one', 'two', 'three'] >>> pkg.mod2.bar() # Модуль 2, функция bar()
Примечание: большая часть статей про пакеты в Python утверждает, что файл __init__.py
должен присутствовать в каталоге пакета при его создании. До версии Python 3.3 это было правдой. Само присутствие файла __init__.py
означало для Python, что пакет был определен. Файл мог содержать код инициализации или даже быть пустым, но он должен был присутствовать.
Начиная с Python 3.3, были введены неявные пространств имен пакета, что позволяют создать пакет без каких-либо __init__.py
файлов. Конечно, файл __init__.py
все еще может присутствовать, если требуется инициализация пакета.
__init__.py
стали необязательны с Python 3.3!Файлы __init__.py
стали необязательны с Python 3.3! Почему же тогда большинство проектов Python все еще используют их?
Представьте себе следующую кодовую базу без каких-либо __init__.py
файлов:
services/ component_a/ one.py component_b/ child/ two.py three.py scripts/ my_script.py
Столкнувшись с этой структурой, можно задаться вопросом, какие из этих каталогов являются пакетами, а какие - просто каталогами, содержащими файлы Python. Это имеет значение не очевидным образом. Возьмем, к примеру, каталог “services”. При этом необходимо импортировать файл one.py
:
import services.component_a.one
;import component_a.one
;Проблема в том, что будет работать только один из вариантов, т. к. внутренние компоненты пакета, скорее всего, предполагают одно или другое. Добавление файлов __init__.py
избавит от догадок и прояснит структуру:
services/ component_a/ __init__.py one.py component_b/ __init__.py child/ __init__.py two.py three.py scripts/ my_script.py
Теперь сразу становится ясно, что component_a
и component_b
являются пакетами, а services
- просто каталогом. Также становится ясно, что папка scripts
- это вообще не пакет и my_script.py
не то, что следует импортировать. Файлы __init__.py
помогают разработчикам понять структуру кодовой базы.
Такие инструменты, как mypy
и ruff
также должны понимать, что является пакетом, а что нет, чтобы работать правильно. По началу можно не заметить проблем, но они могут возникнуть позже, по мере роста кодовой базы. Исправление этих проблем может стать настоящей головной болью, особенно если не знать тонкостей системы импорта Python. Пропуская __init__.py
файлы, можно заложить в свою кодовую базу бомбу замедленного действия.
При исключении __init__.py
файлов фактически создается то, что называется неявным "пространством имен пакета". Это имеет некоторые преимущества, например, позволяет разделить пакет по нескольким каталогам. Если используются пакеты пространства имен, то нужно будет решать проблемы совместимости инструментов и путаницы разработчиков. По этой причине неявное "пространство имен пакета" встречаются редко.