@mkone112
Начинающий питонист.

Стоит ли размещать логику импортов в __init__.py?

Упрощенный пример - имею такую структуру приложения:

config/ # Все конфиги проекта хранятся в одной отдельной директории.
├── __init__.py
└── settings/  # Все настройки
  ├── __init__.py   # Пустой
  ├── base.py  # содержит настройки общие для development и production и указывает на конфиги *.toml
import dynaconf
...
# BASE SETTINGS
# -----------------------------------------------------------------------------

SETTING_0 = 'value1'
SETTING_1 = 'value2'
...

# DYNACONF
# -----------------------------------------------------------------------------

# HERE STARTS DYNACONF EXTENSION LOAD (Keep at the very bottom of base.py)
# Read more at https://www.dynaconf.com/
settings = dynaconf.Dynaconf(
    __name__,
    # Эта переменная будет указывать какие настройки использовать(prod/dev).
    ENV_SWITCHER_FOR_DYNACONF='EXAMPLE_PROJECT_ENV',
    settings_files=['development.toml', 'production.toml'],
)
# HERE ENDS DYNACONF EXTENSION LOAD (No more code below this line)
  ├── development.toml  # Настройки для dev среды.

...
DATABASES__default__ENGINE = 'sqlite3'
DATABASES__default__NAME = '@format {this.PROJECT_ROOT_DIR}/db.sqlite3'
...

  └── production.toml  # Настройки для прода, аналогичны `development.toml`.
main.py

Теперь в приложении я могу:
...
# Указать путь к настройкам:
os.environ.setdefault('PROJECT_SETTINGS_MODULE', 'config.settings.base')  # `config.settings.base` выглядит гораздо менее логично чем `config.settings`.
# Использовать переменную окружения для выбора конфигурации:
os.environ.setdefault('EXAMPLE_PROJECT_ENV', 'production')
...

Проблема: Для указания пакета настроек я использую путь `config.settings.base`, хотя base позволяет получить доступ к dev/prod настройкам, а не только базовым - имя `base` в данном случае не соответствует назначению объекта, к тому-же base.py вроде-как должен хранить только настройки, а он еще и содержит логику с указанием пути к остальным конфигам - на мой взгляд это нарушение SRP.

Решение: импорт всего содержимого `base.py` в `settings/__init__.py`, заодно перенеся всю логику загрузки конфигов, тем самым оставив в `base.py` только базовые настройки.

config/
├── __init__.py
└── settings/
  ├── __init__.py  # Теперь все настройки получаются отсюда.

from .base import *
import dynaconf

# HERE STARTS DYNACONF EXTENSION LOAD (Keep at the very bottom of other
# settings).
# Read more at https://www.dynaconf.com/
settings = dynaconf.Dynaconf(
    __name__,
    ENV_SWITCHER_FOR_DYNACONF='EXAMPLE_PROJECT_ENV',
    settings_files=['development.toml', 'production.toml'],
)
# HERE ENDS DYNACONF EXTENSION LOAD (No more code below this line)

  ├── base.py  # теперь содержит только настройки общие для development/production и минимум логики.

...
# BASE SETTINGS
# -----------------------------------------------------------------------------

SETTING_0 = 'value1'
SETTING_1 = 'value2'
...

  ├── development.toml
  └── production.toml
main.py

Преимущества:
  • Доступ к настройкам логичнее:
    from config import settings
    ...
    settings.DATABASES['default']['ENGINE']

    вместо
    from config import settings
    ...
    settings.base.DATABASES['default']['ENGINE']

  • `base.py` содержит только базовые настройки

Недостатки:
  • Наличие логики в __init__.py

Вопрос: является ли логика в __init__.py - плохой практикой? Для меня вот не очень привычно что там что-то лежит. Кто-то говорит, что это нормально, кто-то что это зло.
  • Вопрос задан
  • 281 просмотр
Решения вопроса 1
@Zanak
Основная задача __init__.py - это настройка импорта твоего пакета в основной код. Ты можешь как угодно тусовать модули и пакеты нижнего уровня и скрыть этот факт за отдачей в виде "from your_package import *" с помощью init файла.
Нет ни чего предосудительного, если в этом файле будует присутствовать код инициализации, но определенную осторожнось соблюдать следует. Например, можно, случайно, получить цикл при импорте модулей. Другой пример, мы используем модели, которые используют соединение с БД, которое открывается позже.
Опять же, возможно это только мои проблемы, я сходу не могу сказать, сколько раз выполнится код инициализации, если модуль импортируется более более 1 раза, и отработает ли ваша логика, если в одном случае это "from your_module import *", в другом "from your_module import name1, name2", а в третьем "import your_module as ym".
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы