Перейти к содержанию

Pydantic поддерживает множество распространенных типов из стандартной библиотеки Python. Если вам нужна более строгая обработка, см. Strict Types , в том числе, если вам нужно ограничить разрешенные значения (например, потребовать положительное int ).

логические значения

Стандартное поле bool вызовет ValidationError , если значение не является одним из следующих:

  • Допустимое логическое значение (т. е. True или False ),
  • Целые числа 0 или 1 ,
  • str , которая при преобразовании в нижний регистр является одной из '0', 'off', 'f', 'false', 'n', 'no', '1', 'on', 't', 'true', 'y', 'yes'
  • bytes , которые действительны в соответствии с предыдущим правилом при декодировании в str

!!! Примечание. Если вам нужна более строгая логическая логика (например, поле, которое допускает только True и False ), вы можете использовать StrictBool .

Вот скрипт, демонстрирующий некоторые из этих вариантов поведения:

from pydantic import BaseModel, ValidationError


class BooleanModel(BaseModel):
    bool_value: bool


print(BooleanModel(bool_value=False))
#> bool_value=False
print(BooleanModel(bool_value='False'))
#> bool_value=False
print(BooleanModel(bool_value=1))
#> bool_value=True
try:
    BooleanModel(bool_value=[])
except ValidationError as e:
    print(str(e))
    """
    1 validation error for BooleanModel
    bool_value
      Input should be a valid boolean [type=bool_type, input_value=[], input_type=list]
    """

Типы даты и времени

Pydantic поддерживает следующие типы даты и времени :

datetime.datetime

  • Поля datetime будут принимать значения типа:

    • datetime ; существующий объект datetime
    • int или float ; предполагается как время Unix, т.е. секунды (если >= -2e10 и <= 2e10 ) или миллисекунды (если < -2e10 или > 2e10 ) с 1 января 1970 г.
    • str ; принимаются следующие форматы:
      • YYYY-MM-DD[T]HH:MM[:SS[.ffffff]][Z or [±]HH[:]MM]
      • YYYY-MM-DD принимается в нестрогом режиме, но не в строгом.
      • int или float как строка (предполагается как время Unix)
    • Экземпляры datetime.date принимаются в нестрогом режиме, но не в строгом.

    from datetime import datetime

    from pydantic import BaseModel

    class Event(BaseModel): dt: datetime = None

    event = Event(dt='2032-04-23T10:20:30.400+02:30')

    print(event.model_dump()) """ {'dt': datetime.datetime(2032, 4, 23, 10, 20, 30, 400000, tzinfo=TzInfo(+02:30))} """

datetime.date

  • Поля date будут принимать значения типа:

    • date ; существующий объект date
    • int или float ; обрабатывается так же, как описано для datetime выше
    • str ; принимаются следующие форматы:
      • YYYY-MM-DD
      • int или float как строка (предполагается как время Unix)

    from datetime import date

    from pydantic import BaseModel

    class Birthday(BaseModel): d: date = None

    my_birthday = Birthday(d=1679616000.0)

    print(my_birthday.model_dump())

    >

datetime.time

  • Поля time будут принимать значения типа:

    • time ; существующий объект time
    • str ; принимаются следующие форматы:
      • HH:MM[:SS[.ffffff]][Z or [±]HH[:]MM]

    from datetime import time

    from pydantic import BaseModel

    class Meeting(BaseModel): t: time = None

    m = Meeting(t=time(4, 8, 16))

    print(m.model_dump())

    >

datetime.timedelta

  • Поля timedelta будут принимать значения типа:

    • timedelta ; существующий объект timedelta
    • int или float ; предполагается, что это секунды
    • str ; принимаются следующие форматы:
      • [-][DD]D[,][HH:MM:]SS[.ffffff]
        • Пример: '1d,01:02:03.000004' или '1D01:02:03.000004' или '01:02:03'
      • [±]P[DD]DT[HH]H[MM]M[SS]S (формат ISO 8601 для временной разницы)

    from datetime import timedelta

    from pydantic import BaseModel

    class Model(BaseModel): td: timedelta = None

    m = Model(td='P3DT12H30M5S')

    print(m.model_dump())

    >

Типы номеров

Pydantic поддерживает следующие числовые типы из стандартной библиотеки Python:

int

  • Pydantic использует int(v) для приведения типов к int ; дополнительные сведения о потере информации во время преобразования данных см. в разделе Преобразование данных .

float

  • Pydantic использует float(v) для приведения значений к числам с плавающей запятой.

enum.IntEnum

  • Проверка: Pydantic проверяет, что значение является допустимым экземпляром IntEnum .
  • Проверка подкласса enum.IntEnum : проверяет, что значение является допустимым членом целочисленного перечисления; более подробную информацию см. в разделе «Перечисления и варианты выбора» .

decimal.Decimal

  • Проверка: Pydantic пытается преобразовать значение в строку, затем передает строку в Decimal(v) .
  • Сериализация: Pydantic сериализует типы Decimal как строки. При желании вы можете использовать собственный сериализатор, чтобы переопределить это поведение. Например:

    from decimal import Decimal

    from typing_extensions import Annotated

    from pydantic import BaseModel, PlainSerializer

    class Model(BaseModel): x: Decimal y: Annotated[ Decimal, PlainSerializer( lambda x: float(x), return_type=float, when_used='json' ), ]

    my_model = Model(x=Decimal('1.1'), y=Decimal('2.1'))

    print(my_model.model_dump()) # (1)!

    >

    print(my_model.model_dump(mode='json')) # (2)!

    >

    print(my_model.model_dump_json()) # (3)!

    >

  • При использовании model_dump оба x и y остаются экземплярами типа Decimal .

  • При использовании model_dump с mode='json' x сериализуется как string , а y сериализуется как float из-за примененного пользовательского сериализатора.
  • Используя model_dump_json, x сериализуется как string , а y сериализуется как float из-за применения специального сериализатора.

Enum

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

enum.Enum проверяет, что значение является допустимым экземпляром Enum . Подкласс enum.Enum проверяет, является ли значение допустимым членом перечисления.

from enum import Enum, IntEnum

from pydantic import BaseModel, ValidationError


class FruitEnum(str, Enum):
    pear = 'pear'
    banana = 'banana'


class ToolEnum(IntEnum):
    spanner = 1
    wrench = 2


class CookingModel(BaseModel):
    fruit: FruitEnum = FruitEnum.pear
    tool: ToolEnum = ToolEnum.spanner


print(CookingModel())
#> fruit=<FruitEnum.pear: 'pear'> tool=<ToolEnum.spanner: 1>
print(CookingModel(tool=2, fruit='banana'))
#> fruit=<FruitEnum.banana: 'banana'> tool=<ToolEnum.wrench: 2>
try:
    CookingModel(fruit='other')
except ValidationError as e:
    print(e)
    """
    1 validation error for CookingModel
    fruit
      Input should be 'pear' or 'banana' [type=enum, input_value='other', input_type=str]
    """

Списки и кортежи

list

Разрешает list, tuple, set, frozenset, deque или генераторы и приводит к list. Если указан общий параметр, соответствующая проверка применяется ко всем элементам списка.

typing.List

Обрабатывается так же, как list выше.

from typing import List, Optional

from pydantic import BaseModel


class Model(BaseModel):
    simple_list: Optional[list] = None
    list_of_ints: Optional[List[int]] = None


print(Model(simple_list=['1', '2', '3']).simple_list)
#> ['1', '2', '3']
print(Model(list_of_ints=['1', '2', '3']).list_of_ints)
#> [1, 2, 3]

tuple

Разрешает list, tuple, set, frozenset, deque или генераторы и приводит к tuple. Если предоставлены общие параметры, соответствующая проверка применяется к соответствующим элементам кортежа.

typing.Tuple

Обрабатывается так же, как tuple выше.

from typing import Optional, Tuple

from pydantic import BaseModel


class Model(BaseModel):
    simple_tuple: Optional[tuple] = None
    tuple_of_different_types: Optional[Tuple[int, float, bool]] = None


print(Model(simple_tuple=[1, 2, 3, 4]).simple_tuple)
#> (1, 2, 3, 4)
print(Model(tuple_of_different_types=[3, 2, 1]).tuple_of_different_types)
#> (3, 2.0, True)

typing.NamedTuple

Подклассы typing.NamedTuple аналогичны tuple , но создают экземпляры данного класса namedtuple .

Подклассы collections.namedtuple аналогичны подклассу typing.NamedTuple, но поскольку типы полей не указаны, все поля считаются имеющими тип Any.

from typing import NamedTuple

from pydantic import BaseModel, ValidationError


class Point(NamedTuple):
    x: int
    y: int


class Model(BaseModel):
    p: Point


try:
    Model(p=('1.3', '2'))
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    p.0
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='1.3', input_type=str]
    """

Дек

deque

Разрешает list, tuple, set, frozenset, deque или генераторы и приводит к deque. Когда предоставляются общие параметры, соответствующая проверка применяется к соответствующим элементам deque .

typing.Deque

Обрабатывается так же, как и deque выше.

from typing import Deque, Optional

from pydantic import BaseModel


class Model(BaseModel):
    deque: Optional[Deque[int]] = None


print(Model(deque=[1, 2, 3]).deque)
#> deque([1, 2, 3])

Наборы

set

Разрешает list, tuple, set, frozenset, deque или генераторы и приводит к set. Если указан общий параметр, соответствующая проверка применяется ко всем элементам набора.

typing.Set

Обрабатывается так же, как set выше.

from typing import Optional, Set

from pydantic import BaseModel


class Model(BaseModel):
    simple_set: Optional[set] = None
    set_of_ints: Optional[Set[int]] = None


print(Model(simple_set={'1', '2', '3'}).simple_set)
#> {'1', '2', '3'}
print(Model(simple_set=['1', '2', '3']).simple_set)
#> {'1', '2', '3'}
print(Model(set_of_ints=['1', '2', '3']).set_of_ints)
#> {1, 2, 3}

frozenset

Разрешает list, tuple, set, frozenset, deque или генераторы и приводит к frozenset. Если указан общий параметр, соответствующая проверка применяется ко всем элементам замороженного набора.

typing.FrozenSet

Обрабатывается так же, как и frozenset выше.

from typing import FrozenSet, Optional

from pydantic import BaseModel


class Model(BaseModel):
    simple_frozenset: Optional[frozenset] = None
    frozenset_of_ints: Optional[FrozenSet[int]] = None


m1 = Model(simple_frozenset=['1', '2', '3'])
print(type(m1.simple_frozenset))
#> <class 'frozenset'>
print(sorted(m1.simple_frozenset))
#> ['1', '2', '3']

m2 = Model(frozenset_of_ints=['1', '2', '3'])
print(type(m2.frozenset_of_ints))
#> <class 'frozenset'>
print(sorted(m2.frozenset_of_ints))
#> [1, 2, 3]

Другие итерации

typing.Sequence

Это предназначено для использования, когда предоставленное значение должно соответствовать требованиям Sequence ABC и желательно выполнить быструю проверку значений в контейнере. Обратите внимание: если необходимо выполнить проверку значений контейнера, тип контейнера может не сохраниться, поскольку проверка может привести к замене значений. Мы гарантируем, что проверенное значение будет допустимым typing.Sequence, но оно может иметь тип, отличный от указанного (обычно это list ).

typing.Iterable

Это предназначено для использования, когда предоставленное значение может быть итерацией, которую не следует использовать. См. «Бесконечные генераторы» ниже для получения более подробной информации о синтаксическом анализе и проверке. Как и в случае с typing.Sequence, мы гарантируем, что проверенный результат будет действительным typing.Iterable, но его тип может отличаться от указанного. В частности, даже если указан тип, не являющийся генератором, например list , значение поля типа typing.Iterable после проверки будет генератором.

Вот простой пример использования typing.Sequence:

from typing import Sequence

from pydantic import BaseModel


class Model(BaseModel):
    sequence_of_ints: Sequence[int] = None


print(Model(sequence_of_ints=[1, 2, 3, 4]).sequence_of_ints)
#> [1, 2, 3, 4]
print(Model(sequence_of_ints=(1, 2, 3, 4)).sequence_of_ints)
#> (1, 2, 3, 4)

Бесконечные генераторы

Если у вас есть генератор, который вы хотите проверить, вы все равно можете использовать Sequence как описано выше. В этом случае генератор будет использован и сохранен в модели в виде списка, а его значения будут проверены на соответствие параметру типа Sequence (например, int в Sequence[int] ).

Однако если у вас есть генератор, который вы не хотите активно использовать (например, бесконечный генератор или удаленный загрузчик данных), вы можете использовать поле типа Iterable:

from typing import Iterable

from pydantic import BaseModel


class Model(BaseModel):
    infinite: Iterable[int]


def infinite_ints():
    i = 0
    while True:
        yield i
        i += 1


m = Model(infinite=infinite_ints())
print(m)
"""
infinite=ValidatorIterator(index=0, schema=Some(Int(IntValidator { strict: false })))
"""

for i in m.infinite:
    print(i)
    #> 0
    #> 1
    #> 2
    #> 3
    #> 4
    #> 5
    #> 6
    #> 7
    #> 8
    #> 9
    #> 10
    if i == 10:
        break

!!! предупреждение. Во время первоначальной проверки Iterable поля выполняют только простую проверку того, что предоставленный аргумент является итеративным. Чтобы предотвратить его использование, проверка полученных значений не выполняется с нетерпением.

Хотя полученные значения не проверяются с особой тщательностью, они все равно проверяются при получении и при необходимости вызывают ValidationError во время получения:

from typing import Iterable

from pydantic import BaseModel, ValidationError


class Model(BaseModel):
    int_iterator: Iterable[int]


def my_iterator():
    yield 13
    yield '27'
    yield 'a'


m = Model(int_iterator=my_iterator())
print(next(m.int_iterator))
#> 13
print(next(m.int_iterator))
#> 27
try:
    next(m.int_iterator)
except ValidationError as e:
    print(e)
    """
    1 validation error for ValidatorIterator
    2
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
    """

Типы картографирования

dict

dict(v) используется для попытки конвертировать словарь. см. typing.Dict ниже для ограничений подтипа.

from pydantic import BaseModel, ValidationError


class Model(BaseModel):
    x: dict


m = Model(x={'foo': 1})
print(m.model_dump())
#> {'x': {'foo': 1}}

try:
    Model(x='test')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    x
      Input should be a valid dictionary [type=dict_type, input_value='test', input_type=str]
    """

typing.Dict

from typing import Dict

from pydantic import BaseModel, ValidationError


class Model(BaseModel):
    x: Dict[str, int]


m = Model(x={'foo': 1})
print(m.model_dump())
#> {'x': {'foo': 1}}

try:
    Model(x={'foo': '1'})
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    x
      Input should be a valid dictionary [type=dict_type, input_value='test', input_type=str]
    """

ТипедДикт

!!! note Примечание. Это новая функция стандартной библиотеки Python, начиная с версии Python 3.8. Из-за ограничений в typing.TypedDict до версии 3.12 для Python <3.12 требуется пакет typing-extensions . Вам нужно будет импортировать TypedDict из typing_extensions вместо typing , и если вы этого не сделаете, вы получите ошибку времени сборки.

TypedDict объявляет тип словаря, который ожидает, что все его экземпляры будут иметь определенный набор ключей, где каждый ключ связан со значением согласованного типа.

Это то же самое, что и dict, но Pydantic проверит словарь, поскольку ключи аннотированы.

from typing_extensions import TypedDict

from pydantic import TypeAdapter, ValidationError


class User(TypedDict):
    name: str
    id: int


ta = TypeAdapter(User)

print(ta.validate_python({'name': 'foo', 'id': 1}))
#> {'name': 'foo', 'id': 1}

try:
    ta.validate_python({'name': 'foo'})
except ValidationError as e:
    print(e)
    """
    1 validation error for typed-dict
    id
      Field required [type=missing, input_value={'name': 'foo'}, input_type=dict]
    """

Вы можете определить __pydantic_config__ для изменения модели, унаследованной от TypedDict. Дополнительные сведения см. в Справочнике ConfigDict API.

from typing import Optional

from typing_extensions import TypedDict

from pydantic import ConfigDict, TypeAdapter, ValidationError


# `total=False` means keys are non-required
class UserIdentity(TypedDict, total=False):
    name: Optional[str]
    surname: str


class User(TypedDict):
    __pydantic_config__ = ConfigDict(extra='forbid')

    identity: UserIdentity
    age: int


ta = TypeAdapter(User)

print(
    ta.validate_python(
        {'identity': {'name': 'Smith', 'surname': 'John'}, 'age': 37}
    )
)
#> {'identity': {'name': 'Smith', 'surname': 'John'}, 'age': 37}

print(
    ta.validate_python(
        {'identity': {'name': None, 'surname': 'John'}, 'age': 37}
    )
)
#> {'identity': {'name': None, 'surname': 'John'}, 'age': 37}

print(ta.validate_python({'identity': {}, 'age': 37}))
#> {'identity': {}, 'age': 37}


try:
    ta.validate_python(
        {'identity': {'name': ['Smith'], 'surname': 'John'}, 'age': 24}
    )
except ValidationError as e:
    print(e)
    """
    1 validation error for typed-dict
    identity.name
      Input should be a valid string [type=string_type, input_value=['Smith'], input_type=list]
    """

try:
    ta.validate_python(
        {
            'identity': {'name': 'Smith', 'surname': 'John'},
            'age': '37',
            'email': 'john.smith@me.com',
        }
    )
except ValidationError as e:
    print(e)
    """
    1 validation error for typed-dict
    email
      Extra inputs are not permitted [type=extra_forbidden, input_value='john.smith@me.com', input_type=str]
    """

возможность вызова

Более подробную информацию о синтаксическом анализе и проверке см. ниже.

Поля также могут иметь тип Callable:

from typing import Callable

from pydantic import BaseModel


class Foo(BaseModel):
    callback: Callable[[int], int]


m = Foo(callback=lambda x: x)
print(m)
#> callback=<function <lambda> at 0x0123456789ab>

!!! предупреждение. Вызываемые поля выполняют только простую проверку возможности вызова аргумента; проверка аргументов, их типов или возвращаемого типа не выполняется.

Типы IP-адресов

  • ipaddress.IPv4Address: использует сам тип для проверки, передавая значение в IPv4Address(v) .
  • ipaddress.IPv4Interface: использует сам тип для проверки, передавая значение в IPv4Address(v) .
  • ipaddress.IPv4Network: использует сам тип для проверки, передавая значение в IPv4Network(v) .
  • ipaddress.IPv6Address: использует сам тип для проверки, передавая значение в IPv6Address(v) .
  • ipaddress.IPv6Interface: использует сам тип для проверки, передавая значение в IPv6Interface(v) .
  • ipaddress.IPv6Network: использует сам тип для проверки, передавая значение в IPv6Network(v) .

См . «Типы сетей», чтобы узнать о других типах IP-адресов.

UUID

Что касается UUID, Pydantic пытается использовать сам тип для проверки, передавая значение UUID(v) . Существует запасной вариант UUID(bytes=v) для bytes и bytearray .

Если вы хотите ограничить версию UUID, вы можете проверить следующие типы:

  • UUID1: требуется UUID версии 1.
  • UUID3: требуется UUID версии 3.
  • UUID4: требуется UUID версии 4.
  • UUID5: требуется UUID версии 5.

Союз

Pydantic имеет обширную поддержку проверки объединения, поддерживаются как typing.Union, так и синтаксис канала Python 3.10 ( A | B ). Подробнее читайте в разделе Unions документации по концепциям.

Type и TypeVar

type

Pydantic поддерживает использование type[T] для указания того, что поле может принимать только классы (не экземпляры), которые являются подклассами T .

[typing.Type . Введите ][]

Обрабатывается так же, как и type выше.

from typing import Type

from pydantic import BaseModel, ValidationError


class Foo:
    pass


class Bar(Foo):
    pass


class Other:
    pass


class SimpleModel(BaseModel):
    just_subclasses: Type[Foo]


SimpleModel(just_subclasses=Foo)
SimpleModel(just_subclasses=Bar)
try:
    SimpleModel(just_subclasses=Other)
except ValidationError as e:
    print(e)
    """
    1 validation error for SimpleModel
    just_subclasses
      Input should be a subclass of Foo [type=is_subclass_of, input_value=<class '__main__.Other'>, input_type=type]
    """

Вы также можете использовать Type , чтобы указать, что разрешен любой класс.

from typing import Type

from pydantic import BaseModel, ValidationError


class Foo:
    pass


class LenientSimpleModel(BaseModel):
    any_class_goes: Type


LenientSimpleModel(any_class_goes=int)
LenientSimpleModel(any_class_goes=Foo)
try:
    LenientSimpleModel(any_class_goes=Foo())
except ValidationError as e:
    print(e)
    """
    1 validation error for LenientSimpleModel
    any_class_goes
      Input should be a type [type=is_type, input_value=<__main__.Foo object at 0x0123456789ab>, input_type=Foo]
    """

typing.TypeVar

TypeVar поддерживается как без ограничений, так и с привязкой.

from typing import TypeVar

from pydantic import BaseModel

Foobar = TypeVar('Foobar')
BoundFloat = TypeVar('BoundFloat', bound=float)
IntStr = TypeVar('IntStr', int, str)


class Model(BaseModel):
    a: Foobar  # equivalent of ": Any"
    b: BoundFloat  # equivalent of ": float"
    c: IntStr  # equivalent of ": Union[int, str]"


print(Model(a=[1], b=4.2, c='x'))
#> a=[1] b=4.2 c='x'

# a may be None
print(Model(a=None, b=1, c=1))
#> a=None b=1.0 c=1

Нет Типы

None, type(None) или Literal[None] эквивалентны в соответствии со спецификацией типизации . Допускается только значение None .

Струны

str : строки принимаются как есть. bytes и bytearray преобразуются с помощью v.decode() . Enum s inheriting from str, are converted using v.value`. Все остальные типы вызывают ошибку.

!!! предупреждение «Строки не являются последовательностями»

While instances of `str` are technically valid instances of the `Sequence[str]` protocol from a type-checker's point of
view, this is frequently not intended as is a common source of bugs.

As a result, Pydantic raises a `ValidationError` if you attempt to pass a `str` or `bytes` instance into a field of type
`Sequence[str]` or `Sequence[bytes]`:


from typing import Optional, Sequence

from pydantic import BaseModel, ValidationError


class Model(BaseModel):
    sequence_of_strs: Optional[Sequence[str]] = None
    sequence_of_bytes: Optional[Sequence[bytes]] = None


print(Model(sequence_of_strs=['a', 'bc']).sequence_of_strs)
#> ['a', 'bc']
print(Model(sequence_of_strs=('a', 'bc')).sequence_of_strs)
#> ('a', 'bc')
print(Model(sequence_of_bytes=[b'a', b'bc']).sequence_of_bytes)
#> [b'a', b'bc']
print(Model(sequence_of_bytes=(b'a', b'bc')).sequence_of_bytes)
#> (b'a', b'bc')


try:
    Model(sequence_of_strs='abc')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    sequence_of_strs
      'str' instances are not allowed as a Sequence value [type=sequence_str, input_value='abc', input_type=str]
    """
try:
    Model(sequence_of_bytes=b'abc')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    sequence_of_bytes
      'bytes' instances are not allowed as a Sequence value [type=sequence_str, input_value=b'abc', input_type=bytes]
    """

Байты

bytes принимаются как есть. bytearray преобразуется с помощью bytes(v) . str преобразуются с помощью v.encode() . int , float и Decimal приводятся с помощью str(v).encode() . См. ByteSize для более подробной информации.

typing.Literal

Pydantic поддерживает использование typing.Literal как упрощенный способ указать, что поле может принимать только определенные литеральные значения:

from typing import Literal

from pydantic import BaseModel, ValidationError


class Pie(BaseModel):
    flavor: Literal['apple', 'pumpkin']


Pie(flavor='apple')
Pie(flavor='pumpkin')
try:
    Pie(flavor='cherry')
except ValidationError as e:
    print(str(e))
    """
    1 validation error for Pie
    flavor
      Input should be 'apple' or 'pumpkin' [type=literal_error, input_value='cherry', input_type=str]
    """

Одним из преимуществ этого типа поля является то, что его можно использовать для проверки равенства одного или нескольких конкретных значений без необходимости объявления пользовательских валидаторов:

from typing import ClassVar, List, Literal, Union

from pydantic import BaseModel, ValidationError


class Cake(BaseModel):
    kind: Literal['cake']
    required_utensils: ClassVar[List[str]] = ['fork', 'knife']


class IceCream(BaseModel):
    kind: Literal['icecream']
    required_utensils: ClassVar[List[str]] = ['spoon']


class Meal(BaseModel):
    dessert: Union[Cake, IceCream]


print(type(Meal(dessert={'kind': 'cake'}).dessert).__name__)
#> Cake
print(type(Meal(dessert={'kind': 'icecream'}).dessert).__name__)
#> IceCream
try:
    Meal(dessert={'kind': 'pie'})
except ValidationError as e:
    print(str(e))
    """
    2 validation errors for Meal
    dessert.Cake.kind
      Input should be 'cake' [type=literal_error, input_value='pie', input_type=str]
    dessert.IceCream.kind
      Input should be 'icecream' [type=literal_error, input_value='pie', input_type=str]
    """

При правильном порядке в аннотированном Union вы можете использовать это для анализа типов с уменьшающейся специфичностью:

from typing import Literal, Optional, Union

from pydantic import BaseModel


class Dessert(BaseModel):
    kind: str


class Pie(Dessert):
    kind: Literal['pie']
    flavor: Optional[str]


class ApplePie(Pie):
    flavor: Literal['apple']


class PumpkinPie(Pie):
    flavor: Literal['pumpkin']


class Meal(BaseModel):
    dessert: Union[ApplePie, PumpkinPie, Pie, Dessert]


print(type(Meal(dessert={'kind': 'pie', 'flavor': 'apple'}).dessert).__name__)
#> ApplePie
print(type(Meal(dessert={'kind': 'pie', 'flavor': 'pumpkin'}).dessert).__name__)
#> PumpkinPie
print(type(Meal(dessert={'kind': 'pie'}).dessert).__name__)
#> Dessert
print(type(Meal(dessert={'kind': 'cake'}).dessert).__name__)
#> Dessert

typing.Any

Допускается любое значение, включая None .

typing.Annotated

Позволяет обернуть другой тип произвольными метаданными согласно PEP-593 . Подсказка Annotated может содержать один вызов функции Field , но в противном случае дополнительные метаданные игнорируются и используется корневой тип.

typing.Pattern

Приведёт к передаче входного значения в re.compile(v) для создания шаблона регулярного выражения.

pathlib.Path

Просто использует сам тип для проверки, передавая значение в Path(v) .


本文总阅读量