Короткий ответ
Enum даёт именованный, замкнутый набор допустимых значений с проверкой на этапе использования через идентичность/принадлежность классу, тогда как обычные строки или числа-константы не защищают от опечаток, произвольных "левых" значений и не группируют связанные значения в единый, самодокументируемый тип.
Как это работает подробнее
class Status(Enum): PENDING = "pending"; PAID = "paid" создаёт класс, где Status.PENDING — это единственный объект-член с заданным значением; сравнение status == Status.PENDING явное и проверяется статическими анализаторами типов, а перечисление всех возможных значений доступно через list(Status). Числовые/строковые "магические" константы (STATUS_PENDING = "pending") синтаксически остаются обычными строками — ничто не мешает присвоить переменной произвольную строку, не входящую в предполагаемый набор.
Пример кода
from enum import Enum
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
CANCELLED = "cancelled"
def can_cancel(status: OrderStatus) -> bool:
return status in (OrderStatus.PENDING, OrderStatus.PAID)
print(can_cancel(OrderStatus.PENDING)) # True
print(OrderStatus.PAID == "paid") # False — enum-член не равен обычной строке
print(OrderStatus.PAID.value == "paid") # True — сравнение по значению явное
print(list(OrderStatus)) # [<OrderStatus.PENDING: 'pending'>, ...]Что использовать на практике
- Практика. Enum улучшает читаемость (
order.status = Status.PAIDнагляднее, чемorder.status = "paid"), позволяет IDE подсказывать все доступные варианты и упрощает рефакторинг (переименование члена enum найдёт все использования). При сериализации в JSON/БД обычно сохраняют.value(например, строку), а при чтении — явно конвертируют обратно в enum черезStatus(value), обрабатывая случай недопустимого значения.
Подводные камни
- Риски. Сравнивают enum-член напрямую со строкой (
status == "paid") вместоstatus == Status.PAID— для обычногоEnumэто обычноFalse, даже если.valueсовпадает (если явно не используетсяStrEnumили(str, Enum)в качестве базовых классов). Также забывают, что добавление нового члена enum в растущей системе — это изменение контракта, которое должно быть согласовано со всеми потребителями значения (БД, другие сервисы), а не только с кодом, где enum объявлен.