Python StrEnum: строковое значение без .value

Категория: Python · Язык: Python · Обновлено:

StrEnum появился в Python 3.11 и делает членов перечисления строками: str(member) возвращает значение, а не имя вида Status.NEW. Такое перечисление удобно передавать в JSON, SQL-параметры и API без ручного .value. Оно также сравнивается с обычной строкой и работает как ключ словаря.

Объявить строковое перечисление

Python
from enum import StrEnum
import json

class Status(StrEnum):
    NEW = "new"
    DONE = "done"

status = Status.NEW
print(status)                              # new
print(status == "new")                     # True
print(json.dumps({"status": status}))      # {"status": "new"}

json.dumps() сериализует член как строку, потому что StrEnum наследуется от str. Обычный Enum в такой ситуации вызвал бы TypeError. При чтении из внешнего источника создавайте член явно: Status("new"); неизвестное значение вызовет ValueError, и это лучше, чем молча принять опечатку.

Если нужен автогенерируемый код, используйте auto(): в StrEnum он берёт имя члена в нижнем регистре. Явные значения всё равно предпочтительнее для стабильного контракта API: переименование поля не должно менять данные, которые уже сохранены в базе.

Сравнить с обычным Enum

Python
from enum import Enum

class LegacyStatus(str, Enum):
    NEW = "new"

print(str(LegacyStatus.NEW))  # LegacyStatus.NEW
print(LegacyStatus.NEW == "new")  # True

Класс str, Enum тоже сравнивается со строкой, но str() возвращает LegacyStatus.NEW, что ломает f-строки и логи. StrEnum решает именно эту проблему. При этом repr() остаётся <Status.NEW: 'new'>, что удобно для отладки.

Не используйте StrEnum там, где важна строгая типизация: равенство с произвольной строкой может скрыть ошибку. Если значение приходит из ненадёжного источника, валидируйте его через конструктор перечисления и возвращайте 400/422, а не пропускайте дальше. Для числовых кодов есть IntEnum, но смешивать его с произвольными числами так же опасно.

Документация: enum.StrEnum, Enum HOWTO.