Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Дискриминированные объединения: Annotated и Tag
discriminated_unions

Дискриминированные объединения: Annotated и Tag

Работа с Union-типами, дискриминаторы для точного выбора модели, валидация полиморфных данных.

Дискриминированные объединения: Annotated и Tag

Дискриминаторы позволяют Pydantic точно выбирать модель из Union по значению поля

#Проблема с обычным Union

Без дискриминатора Pydantic пробует модели по порядку:

from typing import Union from pydantic import BaseModel class Cat(BaseModel): pet_type: str # "cat" meows: int class Dog(BaseModel): pet_type: str # "dog" barks: float Pet = Union[Cat, Dog] # Pydantic пробует Cat первым # Если данные подходят Cat, но должны быть Dog — ошибка pet = Pet(pet_type="dog", barks=5.5) # Может выдать ошибку

#Решение: дискриминированный Union

Используем Field с discriminator:

from typing import Union, Literal from pydantic import BaseModel, Field from typing import Annotated class Cat(BaseModel): pet_type: Literal["cat"] meows: int class Dog(BaseModel): pet_type: Literal["dog"] barks: float # Дискриминированный Union Pet = Annotated[ Union[Cat, Dog], Field(discriminator="pet_type") ] # Pydantic использует pet_type для выбора модели cat = Pet(pet_type="cat", meows=5) # Cat dog = Pet(pet_type="dog", barks=3.5) # Dog print(type(cat)) # <class '__main__.Cat'> print(type(dog)) # <class '__main__.Dog'>

#Как работает дискриминатор

Pydantic смотрит на поле-дискриминатор и выбирает модель:

from typing import Union, Literal from pydantic import BaseModel, Field from typing import Annotated class Cat(BaseModel): pet_type: Literal["cat"] meows: int class Dog(BaseModel): pet_type: Literal["dog"] barks: float Pet = Annotated[Union[Cat, Dog], Field(discriminator="pet_type")] # Валидация из dict data = {"pet_type": "cat", "meows": 10} pet = Pet(**data) # Cat # Ошибка: pet_type не соответствует ни одной модели try: Pet(pet_type="bird", sings=True) except Exception as e: print(e) # ValidationError: Input tag 'bird' doesn't match any tag

#Преимущества дискриминированных Union

#1. Точные ошибки

# Без дискриминатора class Cat(BaseModel): pet_type: str meows: int class Dog(BaseModel): pet_type: str barks: float Pet = Union[Cat, Dog] # Непонятная ошибка try: Pet(pet_type="dog", meows=5) except Exception as e: print(e) # 2 validation errors for Union[Cat, Dog] # ... много текста ... # С дискриминатором class Cat(BaseModel): pet_type: Literal["cat"] meows: int class Dog(BaseModel): pet_type: Literal["dog"] barks: float Pet = Annotated[Union[Cat, Dog], Field(discriminator="pet_type")] # Точная ошибка try: Pet(pet_type="cat", barks=5) # barks нет в Cat except Exception as e: print(e) # 1 validation error for Cat # barks: Extra inputs are not permitted

#2. Производительность

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

#3. Явность кода

Код явно указывает, как выбирается модель.

#Вложенные дискриминированные Union

from typing import Union, Literal from pydantic import BaseModel, Field from typing import Annotated class Cat(BaseModel): pet_type: Literal["cat"] meows: int class Dog(BaseModel): pet_type: Literal["dog"] barks: float class Bird(BaseModel): pet_type: Literal["bird"] sings: bool Pet = Annotated[Union[Cat, Dog, Bird], Field(discriminator="pet_type")] class Owner(BaseModel): name: str pet: Pet owner = Owner( name="Alice", pet={"pet_type": "dog", "barks": 5.5} ) print(owner.pet) # Dog(pet_type='dog', barks=5.5)

#Tag: явное указание тега

Можно использовать Tag для явного указания тега модели:

from typing import Union, Literal from pydantic import BaseModel, Field, Tag from typing import Annotated class Cat(BaseModel): pet_type: Literal["cat"] meows: int class Dog(BaseModel): pet_type: Literal["dog"] barks: float # Явные теги Pet = Annotated[ Union[ Annotated[Cat, Tag("cat")], Annotated[Dog, Tag("dog")] ], Field(discriminator="pet_type") ]

#Дискриминатор во вложенном поле

from typing import Union, Literal from pydantic import BaseModel, Field from typing import Annotated class ErrorDetails(BaseModel): code: str message: str class ValidationErrorDetail(BaseModel): type: Literal["validation"] field: str error: str class SystemErrorDetail(BaseModel): type: Literal["system"] component: str traceback: str ErrorDetail = Annotated[ Union[ValidationErrorDetail, SystemErrorDetail], Field(discriminator="type") ] class APIError(BaseModel): status: int detail: ErrorDetail # Валидация error1 = APIError( status=400, detail={"type": "validation", "field": "email", "error": "invalid"} ) error2 = APIError( status=500, detail={"type": "system", "component": "database", "traceback": "..."} ) print(error1.detail.field) # "email" print(error2.detail.component) # "database"

#Сериализация дискриминированных Union

from typing import Union, Literal from pydantic import BaseModel, Field from typing import Annotated class Cat(BaseModel): pet_type: Literal["cat"] meows: int class Dog(BaseModel): pet_type: Literal["dog"] barks: float Pet = Annotated[Union[Cat, Dog], Field(discriminator="pet_type")] cat = Cat(pet_type="cat", meows=5) dog = Dog(pet_type="dog", barks=3.5) # Сериализация print(cat.model_dump()) # {'pet_type': 'cat', 'meows': 5} print(dog.model_dump()) # {'pet_type': 'dog', 'barks': 3.5} # В Union pets = [cat, dog] for pet in pets: print(pet.model_dump())

#Мини-проект: Система уведомлений

Создайте систему уведомлений с дискриминированными Union:

from typing import Union, Literal, List from pydantic import BaseModel, Field from typing import Annotated from datetime import datetime # EmailNotification # - type: Literal["email"] # - to: str (email) # - subject: str # - body: str # SMSNotification # - type: Literal["sms"] # - to: str (phone) # - message: str # PushNotification # - type: Literal["push"] # - device_id: str # - title: str # - body: str # Notification = Annotated[Union[...], Field(discriminator="type")] # NotificationBatch # - notifications: list[Notification] # - scheduled_at: datetime # - priority: Literal["low", "normal", "high"] # Пример: # batch = NotificationBatch( # notifications=[ # {"type": "email", "to": "alice@example.com", "subject": "Hello", "body": "Hi!"}, # {"type": "sms", "to": "+1234567890", "message": "Alert!"}, # {"type": "push", "device_id": "dev-123", "title": "Update", "body": "New version"} # ], # scheduled_at=datetime.now(), # priority="high" # )

Далее: Pydantic Settings: управление настройками