Сегодня разберем тот случай, когда бизнес приходит с очередной задачей, звучащей так: «Нам нужно, чтобы награды для пользователей можно было гибко менять, подстраивать, проверять и, главное, не ломать всё при каждом чихе».

Pydantic — это библиотека для Python, которая превращает данные в нормальные структуры и валидирует их. В отличие от обычных dataclass'ов, он понимает, что данные редко бывают идеальными, и может ловить их на месте.

Например, вот классический кейс:

  • Пользователь совершил действие.

  • На сервер улетает JSON: «я убил дракона, дай мне 100 золотых».

  • Вы проверяете данные, создаете награду, отправляете пользователю.

Идея в том, чтобы не проверять такие данные вручную через if'ы, а построить модель, которая сама все расскажет. И бонусом — чтобы вся логика не развалилась при следующем обновлении.

Простой пример: валидируем данные награды.

кОfrom pydantic import BaseModel, Field

class Reward(BaseModel):
    type: str
    amount: int = Field(..., gt=0, description="Количество награды (должно быть больше нуля)")

reward = Reward(type="gold", amount=100)
print(reward)

Код проверяет, что amount больше нуля, а так же генерирует документацию для полей (например, description). Если данные некорректны, бросает исключение.


Пример ситуации

Описываем типы наград

Будет три базовых типа наград:

  1. Золото.

  2. Очки опыта.

  3. Скидки.

Каждый тип имеет свои уникальные параметры. Схема Pydantic для этого:

from typing import Union
from pydantic import BaseModel, Field

class GoldReward(BaseModel):
    type: str = Field("gold", const=True)
    amount: int = Field(..., gt=0, description="Количество золота")

class XPReward(BaseModel):
    type: str = Field("xp", const=True)
    points: int = Field(..., gt=0, description="Очки опыта")

class DiscountReward(BaseModel):
    type: str = Field("discount", const=True)
    percentage: float = Field(..., gt=0, lt=100, description="Скидка в процентах")

Reward = Union[GoldReward, XPReward, DiscountReward]

Проверяем данные

Допустим, система присылает JSON с наградой. Pydantic превращает его в объект Python.

reward_data = {"type": "gold", "amount": 150}

# Валидируем и преобразуем
try:
    reward = Reward.parse_obj(reward_data)
    print(reward)
except ValidationError as e:
    print(f"Ошибка валидации: {e}")

Динамическая адаптация наград

Теперь сделаем адаптер, который меняет награду в зависимости от условий. Например:

  • VIP‑пользователи получают удвоенное золото.

  • Скидки выше 50% требуют дополнительного подтверждения.

class User(BaseModel):
    id: int
    is_vip: bool = False

class RewardAdapter:
    def __init__(self, reward: Reward, user: User):
        self.reward = reward
        self.user = user

    def adapt(self):
        if isinstance(self.reward, GoldReward) and self.user.is_vip:
            self.reward.amount *= 2
        elif isinstance(self.reward, DiscountReward) and self.reward.percentage > 50:
            print("Внимание: скидка требует одобрения!")
        return self.reward

Пример использования:

user = User(id=42, is_vip=True)
reward_data = {"type": "gold", "amount": 100}
reward = Reward.parse_obj(reward_data)

adapter = RewardAdapter(reward, user)
adapted_reward = adapter.adapt()

print(adapted_reward)

Добавляем новые типы наград

Что если завтра появляется новый тип награды? Например, «бонусный доступ» к платным функциям. Достаточно просто добавить еще одну модель.

class AccessReward(BaseModel):
    type: str = Field("access", const=True)
    feature: str = Field(..., description="Название функции")

Reward = Union[GoldReward, XPReward, DiscountReward, AccessReward]

Покрываем тестами

Тесты спасают от сюрпризов. Покажу простой тест на VIP‑адаптацию:

def test_vip_gold_reward():
    user = User(id=1, is_vip=True)
    reward = GoldReward(type="gold", amount=100)
    adapter = RewardAdapter(reward, user)
    adapted = adapter.adapt()
    assert adapted.amount == 200

Прочая информация

1. А как с безопасностью данных?

Pydantic позволяет строго ограничивать входные данные. Например:

  • Указываем диапазоны (gt=0, lt=100).

  • Используем const=True, чтобы запрещать некорректные типы.

2. Можно ли использовать Pydantic с FastAPI?

Да! FastAPI поддерживает Pydantic для описания запросов и ответов.

3. А если бизнес‑логика станет сложнее?

Добавляйте адаптеры для каждого нового условия. Pydantic позволяет строить вложенные схемы и обрабатывать их в адаптерах.

С прочими возможностями pydantic можно ознакомиться здесь.


17 декабря в рамках курса "Reinforcement Learning" пройдет открытый урок «Алгоритмы с подкреплением в стохастических играх». Узнать подробности и записаться можно по ссылке.

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


  1. prog420
    12.12.2024 04:56

    Почему это в хабе и с тегами по машинному обучению? Тут от RL только слово "награда".