Задать вопрос
1na1
@1na1
Hello, world!

Как хранить константы в python?

Добрый день!

Я не программист, а прикладной админ. Увлекаюсь написанием инструментов: скриптопись, толстые клиенты и т.д.

У меня в коде часто возникает потребность хранения большого количества константных значений. Таких как спецификация, длинны полей, адреса машин, имена файлов и многое другое. Мне нужно хранилище неизменяемых данных, которые задаются раз и навсегда и доступны из любой точки кода. При этом для меня очень важно не возиться с созданием объекта, не знать какой тип объекта я использую, не менять значения. По сути мне нужен доступный конфиг, чтобы уйти от сырых строк (литералов) в коде.

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

Ниже привожу пример того, что использую

Как я это решаю

Создаем статитчный класс и используем его дефолтные параметры как хранилище данных. Объект такого класса никогда не создается, а данные не меняются.

# Класс хранит допустимое время выполнения процессов
# На самом деле класс может быть более на одну тему, например длинны строк какого-нибудь файла для нарезки

class TimeFrames:
    FILE_EXCHANGE: int = 30
    DATABASE_REQUEST: int = 30
    API_CALL: int = 10
    MAIL_DELIVERY: int = 180


# Система мониторинга использует эти данные

from ... import TimeFrames
# Some code
if api_call_time > TimeFrames.API_CALL:
    alarm()


# Модуль переезда на резерв использует эти данные

from ... import TimeFrames
# Some code
if  request_time > TimeFrames.DATABASE_REQUEST:
    move_to_dr()


И многие другие системы используют такой подход.

Также я использую эти константы

  • Как ключи словаря - dict.fromkeys([DbServers.MAIN_DB, DbServers.DR_DB], '')
  • Для подсказки функциям направления работы
  • Для хранения простых sql-запросов для проброса в БД
  • Для создания именованных пользовальских типов чего угодно - CheckBoxType.GET_JSON, CheckBoxType.I_AM_AGREE и др.
  • Многое другое


Почему не Enum, dataclass, что-то еще

Мне ужасно неудобно использовать Enum из-за требований постоянно помнить, что это именно Enum. У него нужно было бы писать TimeFrames.API_CALL.value и постоянно держать в голове эту особенность.

dataclass требует создавать из него объекты, упрощая инициализацию и др. Честно говоря мне dataclass видится как хранилище именно не-констант, а переменных значений, которые можно упаковать в разные экземпляры на лету.

Для хранения и валидации сложных структур я использую JSON и pydantic. Совсем сложные, комплексные данные - в БД.

Мой вопрос: какой механизм лучше всего использовать для хранения и быстрого получения предопределенных данных? Является ли мой подход адекватным и будет ли понятен программисту? Какие неочевидные минусы я упустил из внимания, используя такой подход?

Буду очень благодарен за ссылки на документы, статьи и Ваше личное мнение. Спасибо!
  • Вопрос задан
  • 2286 просмотров
Подписаться 1 Простой Комментировать
Решения вопроса 1
Maksim_64
@Maksim_64
Data Analyst
Вот большой гайд о константах, Там в принципе описаны все возможные способы создания констант, а их много. И самое главное, есть раздел Handling Your Constants in a Real-World Project, то что тебе и нужно, а не просто "знакомство". Ресурс в python среде пользуется репутацией, его даже в продвинутых книгах упоминают.
https://realpython.com/python-constants/
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
trapwalker
@trapwalker Куратор тега Python
Программист, энтузиаст
Ваш подход вполне пригоден к использованию и его можно применять.
Тут надо внести ясность, что есть разница между константами и настройками. Настройки предполагается менять, их иногда "перекрывают" через параметры командной строки или переменные среды окружения.
Если вам нужны настройки, то этот вариант не годится. Используйте предложенный выше вариант с Pydantic.
Если это именно константы, их не потребуется или не скоро потребуется перекрывать, загружать из файлов, пробрасывать через переменные среды окружения, через параметры командной строки и т.д.
Если значения этих констант приемлемо хранить в репозитории, то нет ничего плохого в том, что вы группируете константы в классы. Это нормально для питона.

Но учтите, что вам может захотеться хранить в таких вот местах какие-то чувствительные, т.н. секретные данные. Например, ключи шифрования, пароли для подключения к БД или другим сервисам, ключи API, всякие токены и т.д. Такие данные опасно, неудобно и неправильно хранить в репозитории вместе с кодом. Для хранения таких параметров тоже нужно использовать библиотеки для хранения, сериализации и десериализации настроек.

Возможно у вас возникнет соблазн сейчас сделать вот так по-простому, а чувствительные данные и секреты тоже импортировать в виде питоновских модулей, но эти модули добавить в игнор-лист репозитория, а импортировать с соответствующим перехватом ошибок импорта... Так можно сделать, если вам критически не хочется брать в проект лишние зависимости, но это тот ещё геморрой. Лучше использовать специальные механизмы хранения настроек сразу. Тем более это потребует меньше кода (который нужно еще и тестировать) и сложности (которую принято выносить и тестировать отдельно, чем такие либы как Pydantic и занимается).
Ответ написан
@blozheupasi
По поводу Enum и постоянного обращения на value. Можно делать так:

class CustomEnum(str, Enum):
    """Помощник, чтобы не писать .value кажыдй раз при вызове enum класса

    Вызов: YourCustomEnum.megaparam # сразу вернет значение, .value в конце не нужно
    """
    def __str__(self) -> str:
        return str.__str__(self)


По поводу хранения и быстрого переопределения параметров в продакшне можно вместо БД (если, конечно, речь о чем-нибудь типа psql или mysql) использовать etcd — он легкий и умеет в HTTP.
Ответ написан
@twistfire92
Python backend developer
Как вариант, можете использовать BaseSettings из Pydantic (Но надо смотреть на версию, во второй отдельная либа для настроек). Получится лишь в ваш класс добавить наследование от BaseSettings. Но если вы не собираетесь прокидывать переменные окружения в настройки, то и ваш вариант вполне рабочий.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

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