← Назад к вопросам

Зачем нужен enum?

1.0 Junior🔥 101 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Enum — перечисления для типобезопасности

Enum (Enumeration) — это класс Python, который позволяет определить набор именованных констант. Это мощный инструмент для создания типобезопасного кода и улучшения его читаемости.

Основная проблема без Enum

Плохой подход — строковые константы

# Это работает, но опасно
user_status = "active"

if user_status == "active":
    print("Пользователь активен")
elif user_status == "inactive":
    print("Пользователь неактивен")
elif user_status == "banned":  # Опечатка!
    print("Пользователь заблокирован")

# Проблемы:
# 1. Легко опечататься: "acctive" вместо "active"
# 2. IDE не может подсказать возможные значения
# 3. Нельзя сделать type checking
# 4. Можно передать любую строку

Хороший подход — Enum

from enum import Enum

class UserStatus(Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    BANNED = "banned"

# Теперь это типобезопасно
user_status = UserStatus.ACTIVE

if user_status == UserStatus.ACTIVE:
    print("Пользователь активен")
elif user_status == UserStatus.INACTIVE:
    print("Пользователь неактивен")

# Ошибку будет видна сразу
# user_status = UserStatus.ACTTIVE  # AttributeError!

Основные преимущества Enum

1. Типобезопасность

from enum import Enum
from typing import Literal

class OrderStatus(Enum):
    PENDING = "pending"
    PROCESSING = "processing"
    SHIPPED = "shipped"
    DELIVERED = "delivered"

class Order:
    def __init__(self, status: OrderStatus):  # Type hint!
        if not isinstance(status, OrderStatus):
            raise TypeError(f"Expected OrderStatus, got {type(status)}")
        self.status = status

# IDE подскажет возможные значения
order = Order(OrderStatus.PENDING)  # ✓ Правильно
order = Order("pending")             # ✗ IDE предупредит

2. Читаемость кода

# Без Enum
if payment_method == "CC":
    charge_credit_card()
elif payment_method == "BTC":
    charge_bitcoin()
elif payment_method == "PP":
    charge_paypal()

# С Enum
class PaymentMethod(Enum):
    CREDIT_CARD = "CC"
    BITCOIN = "BTC"
    PAYPAL = "PP"

if payment_method == PaymentMethod.CREDIT_CARD:
    charge_credit_card()
elif payment_method == PaymentMethod.BITCOIN:
    charge_bitcoin()
elif payment_method == PaymentMethod.PAYPAL:
    charge_paypal()

# Второй вариант очевидно более читаем!

3. Автодополнение в IDE

class Role(Enum):
    ADMIN = "admin"
    USER = "user"
    MODERATOR = "moderator"

# IDE покажет все доступные значения
role = Role.  # Появится список: ADMIN, USER, MODERATOR

4. Валидация на уровне типов

from typing import Protocol

class UserStatus(Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    BANNED = "banned"

def update_user_status(user_id: int, status: UserStatus) -> None:
    """Только Enum значения приемлются"""
    # mypy проверит типы
    pass

update_user_status(1, UserStatus.ACTIVE)  # ✓ OK
update_user_status(1, "active")            # ✗ mypy ошибка

Практические примеры

Пример 1: Статусы в БД

from enum import Enum
from sqlalchemy import Column, String, Enum as SQLEnum

class OrderStatus(Enum):
    NEW = "new"
    CONFIRMED = "confirmed"
    SHIPPED = "shipped"
    DELIVERED = "delivered"
    CANCELLED = "cancelled"

class Order(Base):
    __tablename__ = "orders"
    
    id = Column(Integer, primary_key=True)
    status = Column(
        SQLEnum(OrderStatus),  # SQLAlchemy знает про Enum
        default=OrderStatus.NEW,
        nullable=False
    )

# Использование
order = Order(status=OrderStatus.NEW)

if order.status == OrderStatus.SHIPPED:
    send_notification()

Пример 2: Роли и permissions

from enum import Enum, auto

class Permission(Enum):
    CREATE = auto()
    READ = auto()
    UPDATE = auto()
    DELETE = auto()

class Role(Enum):
    ADMIN = {
        Permission.CREATE,
        Permission.READ,
        Permission.UPDATE,
        Permission.DELETE
    }
    EDITOR = {
        Permission.CREATE,
        Permission.READ,
        Permission.UPDATE
    }
    VIEWER = {
        Permission.READ
    }

class User:
    def __init__(self, role: Role):
        self.role = role
    
    def can(self, permission: Permission) -> bool:
        return permission in self.role.value

# Использование
admin = User(Role.ADMIN)
print(admin.can(Permission.DELETE))  # True

viewer = User(Role.VIEWER)
print(viewer.can(Permission.DELETE))  # False

Пример 3: API для Pydantic

from enum import Enum
from pydantic import BaseModel, field_validator

class ProductCategory(Enum):
    ELECTRONICS = "electronics"
    CLOTHING = "clothing"
    FOOD = "food"
    HOME = "home"

class Product(BaseModel):
    name: str
    category: ProductCategory  # Type safe!
    price: float
    
    @field_validator('price')
    @classmethod
    def price_must_be_positive(cls, v):
        if v <= 0:
            raise ValueError('Price must be positive')
        return v

# Использование
product_data = {
    "name": "Laptop",
    "category": "electronics",  # Строка автоматически преобразуется
    "price": 999.99
}

product = Product(**product_data)
print(product.category)  # ProductCategory.ELECTRONICS
print(product.category.value)  # "electronics"

Пример 4: Сравнение и итерация

from enum import Enum

class Environment(Enum):
    DEVELOPMENT = "dev"
    STAGING = "staging"
    PRODUCTION = "prod"

# Сравнение
env = Environment.DEVELOPMENT

if env == Environment.DEVELOPMENT:
    print("Режим разработки")

# Получение значения
print(env.name)     # "DEVELOPMENT"
print(env.value)    # "dev"

# Итерация по всем значениям
for env in Environment:
    print(f"{env.name}: {env.value}")

# Преобразование из строки
env_str = "prod"
env = Environment(env_str)  # Найдет PRODUCTION

# Преобразование по имени
env = Environment["DEVELOPMENT"]  # Найдет DEVELOPMENT

Пример 5: IntEnum для числовых значений

from enum import IntEnum

class HttpStatus(IntEnum):
    OK = 200
    CREATED = 201
    BAD_REQUEST = 400
    UNAUTHORIZED = 401
    NOT_FOUND = 404
    SERVER_ERROR = 500

# IntEnum позволяет сравнивать с числами
if HttpStatus.OK == 200:  # True!
    print("OK")

if HttpStatus.OK > 100:  # True! Сравнение как с числом
    print("Success code")

status_code = HttpStatus.NOT_FOUND
print(status_code + 1)  # 405 (можно арифметика)

Пример 6: Flag для битовых флагов

from enum import Flag, auto

class Permission(Flag):
    READ = auto()      # 1
    WRITE = auto()     # 2
    EXECUTE = auto()   # 4
    DELETE = auto()    # 8

# Комбинирование флагов
user_permissions = Permission.READ | Permission.WRITE

# Проверка флагов
if Permission.READ in user_permissions:
    print("Может читать")

if Permission.DELETE in user_permissions:
    print("Может удалять")
else:
    print("Не может удалять")

Когда использовать Enum

Используй Enum когда:

  • Есть фиксированный набор значений
  • Значения не меняются во время выполнения
  • Нужна типобезопасность
  • Хочешь улучшить читаемость
  • Используешь mypy для проверки типов
  • Работаешь со статусами, ролями, категориями

НЕ используй Enum когда:

  • Значения динамические (загружаются из БД)
  • Много возможных значений
  • Значения часто меняются

Итог

Enum — это мощный инструмент для:

  1. Типобезопасности — IDE и mypy проверяют
  2. Читаемости — явные имена вместо волшебных строк
  3. Автодополнения — IDE подсказывает варианты
  4. Валидации — нельзя передать неправильное значение
  5. Поддержки — будущие разработчики поймут код сразу

Это best practice в современной разработке на Python.