<Зачем нужен файл __init__.py в проектах на Python>

Раздел: Структура проекта -> Модули и пакеты

Основное назначение и использование __init__.py

Файл __init__.py в Python служит маркером того, что директория является пакетом. Он может быть пустым или содержать код инициализации. При импорте пакета Python выполняет этот файл, что позволяет задать начальное состояние пакета, импортировать вложенные модули и определить переменную __all__ для контроля экспорта.

# Пример пустого файла __init__.py
# Директория my_package/
# my_package/__init__.py (пустой)
# my_package/module1.py

# Внешний код:
import my_package
# работает, my_package считается пакетом

импорт кода python (импорт кода python)

Как сделать импорт удобным: пробросить имена из модулей на уровень пакета?

Поместите в __init__.py импорт нужных объектов из подмодулей. Тогда пользователь сможет обращаться к ним напрямую через пакет.

# my_package/__init__.py
from .module1 import useful_function
from .module2 import UsefulClass

# Теперь import my_package даёт доступ:
# my_package.useful_function()
# my_package.UsefulClass

ошибка импорта python (ошибка импорта модуля в python)

Проблема: если модули импортируют друг друга, возникает циклический импорт. Решение – использовать отложенный импорт внутри функций или переместить код в отдельный модуль.

Как ограничить список экспортируемых символов?

Определите в __init__.py переменную __all__ – список строк с именами, которые будут импортированы при from package import *.

# my_package/__init__.py
__all__ = ['useful_function', 'UsefulClass']

from .module1 import useful_function
from .module2 import UsefulClass, _private_function
# _private_function не попадёт в __all__

Python package init py (инициализация пакета с __init__.py)

# В консоли:
>>> from my_package import *
>>> useful_function

>>> _private_function
NameError: name '_private_function' is not defined

Python создание модулей (создание модулей в python)

Типичная ошибка: забыть добавить имя в __all__ после расширения пакета. Это ломает явный экспорт, но не влияет на точечный импорт.

Как выполнить инициализацию пакета при его импорте?

Напишите код, который выполняется один раз при первом импорте пакета. Например, настройка логирования, проверка версий зависимостей или создание глобальных объектов.

# my_package/__init__.py
import logging

logging.getLogger(__name__).addHandler(logging.NullHandler())

# Или проверка версий:
import sys
if sys.version_info < (3, 8):
    raise RuntimeError('my_package requires Python 3.8+')

# Создание единственного экземпляра:
from .config import Config
config = Config()

Python init file (файл __init__.py в python)

Проблема: если инициализация затрагивает ресурсы (файлы, сеть), импорт может стать медленным или упасть с ошибкой. Переносите тяжёлые операции в отдельные функции, вызываемые по необходимости.

Как автоматически импортировать все подмодули при обращении к пакету?

Можно в __init__.py перебрать все файлы в текущей директории и импортировать их. Это удобно для плагинов или динамической загрузки.

# my_package/__init__.py
import importlib
import pkgutil

__all__ = []
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
    __all__.append(module_name)
    _module = importlib.import_module('.' + module_name, __package__)
    # Можно пробросить атрибуты в глобальное пространство пакета
    for attr_name in dir(_module):
        if not attr_name.startswith('_'):
            globals()[attr_name] = getattr(_module, attr_name)

Ошибка: импорт всех модулей может привести к циклическим зависимостям и неожиданным побочным эффектам. Используйте такую технику только для простых модулей без взаимных импортов.

Как сделать пакет без __init__.py (namespace package)?

В Python 3.3+ пакет может существовать без файла __init__.py. Это называется пространством имён (namespace package). Он позволяет объединять несколько директорий с одинаковым именем пакета в разных частях файловой системы.

# Директория project1/pkg/submodule.py
# Директория project2/pkg/other.py
# Ни в одной из них нет __init__.py
# При import pkg.submodule; import pkg.other оба модуля доступны
# pkg ведёт себя как пакет, но не имеет собственных атрибутов

Проблема: такой пакет не может содержать код инициализации, атрибуты пакета или __all__. При попытке обратиться к атрибуту, не связанному с подмодулем, возникнет AttributeError.

Расширенные примеры и сценарии использования

Пример 1. Реестр плагинов через __init__.py

Создадим пакет plugins, который автоматически регистрирует все классы, наследуемые от базового класса BasePlugin.

Пример
# plugins/__init__.py
import importlib
import pkgutil
from .base import BasePlugin

_registry = {}

def register_plugin(name, cls):
    _registry[name] = cls

def get_plugin(name):
    return _registry.get(name)

# При импорте пакета автоматически загружаем все модули и регистрируем плагины
for loader, module_name, is_pkg in pkgutil.iter_modules(__path__):
    module = importlib.import_module('.' + module_name, __package__)
    for attr_name in dir(module):
        attr = getattr(module, attr_name)
        if isinstance(attr, type) and issubclass(attr, BasePlugin) and attr is not BasePlugin:
            register_plugin(module_name + '.' + attr_name, attr)

# plugins/base.py
class BasePlugin:
    pass

# plugins/print_plugin.py
from . import BasePlugin

class PrintPlugin(BasePlugin):
    def run(self):
        print('Hello from PrintPlugin')

# plugins/math_plugin.py
from . import BasePlugin

class MathPlugin(BasePlugin):
    def run(self):
        return 2 + 2
# Внешнее использование:
>>> from plugins import get_plugin
>>> plugin_cls = get_plugin('print_plugin.PrintPlugin')
>>> plugin = plugin_cls()
>>> plugin.run()
Hello from PrintPlugin

Пример 2. Настройка логирования для пакета

Используем __init__.py, чтобы добавить логгер с обработчиком, который можно переопределить в приложении.

Пример
# mypackage/__init__.py
import logging

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())  # По умолчанию ничего не выводим

# Но если пользователь хочет видеть логи, он может настроить:
# logging.basicConfig(level=logging.INFO)
# import mypackage  # теперь логи mypackage будут печататься

Такой подход предотвращает дублирование сообщений при импорте из разных мест.

Пример 3. Импорт с __all__ и вложенные пакеты

Представим структуру mypackage/subpackage/. В subpackage/__init__.py укажем __all__ и импортируем модули.

Пример
# mypackage/__init__.py
from . import subpackage
from .submodule import public_function

# mypackage/subpackage/__init__.py
__all__ = ['submodule1', 'submodule2']
from . import submodule1, submodule2

# mypackage/subpackage/submodule1.py
def func1(): pass

# Внешний код:
>>> from mypackage.subpackage import *
>>> func1()  # ошибка? func1 не в __all__
# чтобы func1 был доступен, нужно или добавить его в __all__ subpackage/__init__.py,
# или импортировать отдельно: from mypackage.subpackage.submodule1 import func1
# Можно также импортировать напрямую:
>>> from mypackage.subpackage.submodule1 import func1
>>> func1()  # работает

Файл __init__.py в Python - comments

En
Python init file (python)