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

Зачем использовать Пидантик?

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

Трудно понять, почему так много людей приняли Pydantic с момента его создания шесть лет назад, но вот несколько предположений.

Подсказки типов для проверки схемы

Схема, по которой проверяется Pydantic, обычно определяется подсказками типов Python.

Подсказки типов отлично подходят для этого, поскольку, если вы пишете на современном Python, вы уже знаете, как их использовать. Использование подсказок по типам также означает, что Pydantic хорошо интегрируется с инструментами статической типизации, такими как mypy иpyright, а также с такими IDE, как pycharm и vscode.

???+ пример «Пример — просто введите подсказки» (Для этого примера требуется Python 3.9+) ```py требует="3.9" при вводе import Annotated, Dict, List, Literal, Tuple

from annotated_types import Gt

from pydantic import BaseModel


class Fruit(BaseModel):
    name: str  # (1)!
    color: Literal['red', 'green']  # (2)!
    weight: Annotated[float, Gt(0)]  # (3)!
    bazam: Dict[str, List[Tuple[int, bool, float]]]  # (4)!


print(
    Fruit(
        name='Apple',
        color='red',
        weight=4.2,
        bazam={'foobar': [(1, True, 0.1)]},
    )
)
#> name='Apple' color='red' weight=4.2 bazam={'foobar': [(1, True, 0.1)]}
```

1. The `name` field is simply annotated with `str` - any string is allowed.
2. The [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal) type is used to enforce that `color` is either `'red'` or `'green'`.
3. Even when we want to apply constraints not encapsulated in python types, we can use [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) and [`annotated-types`](https://github.com/annotated-types/annotated-types) to enforce constraints without breaking type hints.
4. I'm not claiming "bazam" is really an attribute of fruit, but rather to show that arbitrarily complex types can easily be validated.

!!! подсказка «Подробнее» См. документацию по поддерживаемым типам.

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

Основная логика проверки Pydantic реализована в отдельном пакете pydantic-core, где проверка большинства типов реализована в Rust.

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

??? example «Пример производительности — Pydantic и выделенный код» В общем, выделенный код должен работать намного быстрее, чем валидатор общего назначения, но в этом примере Pydantic на >300% быстрее выделенного кода при анализе JSON и проверке URL-адресов.

```py title="Performance Example"
import json
import timeit
from urllib.parse import urlparse

import requests

from pydantic import HttpUrl, TypeAdapter

reps = 7
number = 100
r = requests.get('https://api.github.com/emojis')
r.raise_for_status()
emojis_json = r.content


def emojis_pure_python(raw_data):
    data = json.loads(raw_data)
    output = {}
    for key, value in data.items():
        assert isinstance(key, str)
        url = urlparse(value)
        assert url.scheme in ('https', 'http')
        output[key] = url


emojis_pure_python_times = timeit.repeat(
    'emojis_pure_python(emojis_json)',
    globals={
        'emojis_pure_python': emojis_pure_python,
        'emojis_json': emojis_json,
    },
    repeat=reps,
    number=number,
)
print(f'pure python: {min(emojis_pure_python_times) / number * 1000:0.2f}ms')
#> pure python: 5.32ms

type_adapter = TypeAdapter(dict[str, HttpUrl])
emojis_pydantic_times = timeit.repeat(
    'type_adapter.validate_json(emojis_json)',
    globals={
        'type_adapter': type_adapter,
        'HttpUrl': HttpUrl,
        'emojis_json': emojis_json,
    },
    repeat=reps,
    number=number,
)
print(f'pydantic: {min(emojis_pydantic_times) / number * 1000:0.2f}ms')
#> pydantic: 1.54ms

print(
    f'Pydantic {min(emojis_pure_python_times) / min(emojis_pydantic_times):0.2f}x faster'
)
#> Pydantic 3.45x faster
```

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

!!! подсказка «Подробнее» Выступление Сэмюэля Колвина на PyCon 2023 объясняет, как работает pydantic-core и как оно интегрируется с Pydantic.

Сериализация

Pydantic предоставляет функциональность для сериализации модели тремя способами:

  1. К словарю Python, состоящему из связанных объектов Python
  2. Для словаря Python, состоящего только из типов «jsonable»
  3. К строке JSON

Во всех трех режимах вывод можно настроить, исключив определенные поля, исключив неустановленные поля, исключив значения по умолчанию и исключив значения «Нет».

??? пример «Пример — 3 способа сериализации» ```py from datetime import datetime

from pydantic import BaseModel


class Meeting(BaseModel):
    when: datetime
    where: bytes
    why: str = 'No idea'


m = Meeting(when='2020-01-01T12:00', where='home')
print(m.model_dump(exclude_unset=True))
#> {'when': datetime.datetime(2020, 1, 1, 12, 0), 'where': b'home'}
print(m.model_dump(exclude={'where'}, mode='json'))
#> {'when': '2020-01-01T12:00:00', 'why': 'No idea'}
print(m.model_dump_json(exclude_defaults=True))
#> {"when":"2020-01-01T12:00:00","where":"home"}
```

!!! подсказка «Подробнее» См. документацию по сериализации.

Схема JSON

Схема JSON может быть создана для любой схемы Pydantic, что позволяет самодокументировать API и интегрировать ее с широким спектром инструментов, поддерживающих схему JSON.

??? пример «Пример — схема JSON» ```py from datetime import datetime

from pydantic import BaseModel Адрес класса (БазоваяМодель): улица: ул. город: ул. Почтовый индекс: ул. Встреча класса (BaseModel): когда: дата и время где: Адрес почему: str = 'Понятия не имею' печать (Meeting.model_json_schema()) """ { ' $defs': { 'Адрес': { 'характеристики': { 'улица': {'title': 'Улица', 'тип': 'строка'}, 'city': {'title': 'Город', 'type': 'string'}, 'zipcode': {'title': 'Zipcode', 'type': 'string'}, }, 'обязательно': ['улица', 'город', 'почтовый индекс'], 'title': 'Адрес', «тип»: «объект», } }, 'характеристики': { 'когда': {'формат': 'дата-время', 'заголовок': 'Когда', 'тип': 'строка'}, 'где': {'$ ref': '#/$defs/Address'}, 'Why': {'default': 'Понятия не имею', 'title': 'Почему', 'type': 'string'}, }, 'обязательно': ['когда', 'где'], 'title': 'Встреча', «тип»: «объект», } """ ```

Pydantic генерирует схему JSON версии 2020-12, последнюю версию стандарта, совместимую с OpenAPI 3.1.

!!! подсказка «Подробнее» См. документацию по схеме JSON.

Строгий режим и принуждение данных

По умолчанию Pydantic толерантен к частым неверным типам и приводит данные к правильному типу — например, числовая строка, переданная в поле int, будет анализироваться как int.

Pydantic также имеет режим strict=True, также известный как «строгий режим», в котором типы не приводятся и возникает ошибка проверки, если входные данные точно не соответствуют схеме или подсказке типа.

Но строгий режим был бы совершенно бесполезен при проверке данных JSON, поскольку у JSON нет типов, соответствующих многим распространённым типам Python, таким как datetime, UUID или байты.

Чтобы решить эту проблему, Pydantic может анализировать и проверять JSON за один шаг. Это позволяет разумно преобразовывать данные, такие как строки RFC3339 (также известные как ISO8601), в объекты datetime. Поскольку анализ JSON реализован в Rust, он также очень эффективен.

??? пример «Пример — строгий режим, который действительно полезен» ```py from datetime import datetime

from pydantic import BaseModel, ValidationError


class Meeting(BaseModel):
    when: datetime
    where: bytes


m = Meeting.model_validate({'when': '2020-01-01T12:00', 'where': 'home'})
print(m)
#> when=datetime.datetime(2020, 1, 1, 12, 0) where=b'home'
try:
    m = Meeting.model_validate(
        {'when': '2020-01-01T12:00', 'where': 'home'}, strict=True
    )
except ValidationError as e:
    print(e)
    """
    2 validation errors for Meeting
    when
      Input should be a valid datetime [type=datetime_type, input_value='2020-01-01T12:00', input_type=str]
    where
      Input should be a valid bytes [type=bytes_type, input_value='home', input_type=str]
    """

m_json = Meeting.model_validate_json(
    '{"when": "2020-01-01T12:00", "where": "home"}'
)
print(m_json)
#> when=datetime.datetime(2020, 1, 1, 12, 0) where=b'home'
```

!!! подсказка «Подробнее» См. документацию по строгому режиму.

Классы данных, TypedDicts и многое другое

Pydantic предоставляет четыре способа создания схем, а также выполнения проверки и сериализации:

  1. BaseModel — собственный суперкласс Pydantic со множеством общих утилит, доступных через методы экземпляра.
  2. pydantic.dataclasses.dataclass — оболочка стандартных классов данных, которая выполняет проверку при инициализации класса данных.
  3. TypeAdapter — общий способ адаптации любого типа для проверки и сериализации. Это позволяет проверять такие типы, как TypedDict и NamedTuple, а также простые скалярные значения, такие как int или timedelta — все поддерживаемые типы можно использовать с TypeAdapter.
  4. validate_call — декоратор для выполнения проверки при вызове функции.

??? пример «Пример — схема на основе TypedDict» ```py from datetime import datetime

from typing_extensions import NotRequired, TypedDict

from pydantic import TypeAdapter


class Meeting(TypedDict):
    when: datetime
    where: bytes
    why: NotRequired[str]


meeting_adapter = TypeAdapter(Meeting)
m = meeting_adapter.validate_python(  # (1)!
    {'when': '2020-01-01T12:00', 'where': 'home'}
)
print(m)
#> {'when': datetime.datetime(2020, 1, 1, 12, 0), 'where': b'home'}
meeting_adapter.dump_python(m, exclude={'where'})  # (2)!

print(meeting_adapter.json_schema())  # (3)!
"""
{
    'properties': {
        'when': {'format': 'date-time', 'title': 'When', 'type': 'string'},
        'where': {'format': 'binary', 'title': 'Where', 'type': 'string'},
        'why': {'title': 'Why', 'type': 'string'},
    },
    'required': ['when', 'where'],
    'title': 'Meeting',
    'type': 'object',
}
"""
```

1. `TypeAdapter` for a `TypedDict` performing validation, it can also validate JSON data directly with `validate_json`
2. `dump_python` to serialise a `TypedDict` to a python object, it can also serialise to JSON with `dump_json`
3. `TypeAdapter` can also generate JSON Schema

Кастомизация

Функциональные валидаторы и сериализаторы, а также мощный протокол для пользовательских типов означают, что способ работы Pydantic можно настроить для каждого поля или типа.

??? example «Пример настройки — валидаторы-обертки» «валидаторы-обертки» являются новой функцией Pydantic V2 и представляют собой один из самых мощных способов настройки проверки Pydantic. ```py from datetime import datetime, timezone

from pydantic import BaseModel, field_validator


class Meeting(BaseModel):
    when: datetime

    @field_validator('when', mode='wrap')
    def when_now(cls, input_value, handler):
        if input_value == 'now':
            return datetime.now()
        when = handler(input_value)
        # in this specific application we know tz naive datetimes are in UTC
        if when.tzinfo is None:
            when = when.replace(tzinfo=timezone.utc)
        return when


print(Meeting(when='2020-01-01T12:00+01:00'))
#> when=datetime.datetime(2020, 1, 1, 12, 0, tzinfo=TzInfo(+01:00))
print(Meeting(when='now'))
#> when=datetime.datetime(2032, 1, 2, 3, 4, 5, 6)
print(Meeting(when='2020-01-01T12:00'))
#> when=datetime.datetime(2020, 1, 1, 12, 0, tzinfo=datetime.timezone.utc)
```

!!! подсказка «Подробнее» См. документацию по валидаторам, пользовательским сериализаторам и пользовательским типам.

Экосистема

На момент написания статьи на GitHub имеется 214 100 репозиториев и 8 119 пакетов на PyPI, которые зависят от Pydantic.

Некоторые известные библиотеки, зависящие от Pydantic:

Дополнительные библиотеки, использующие Pydantic, можно найти по адресу Kludex/awesome-pydantic.

Организации, использующие Pydantic

Некоторые известные компании и организации, использующие Pydantic, вместе с комментариями о том, почему и откуда мы знаем, что они используют Pydantic.

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

  • Использование pydantic в качестве зависимости в публичном репозитории
  • Перенаправление трафика на сайт документации pydantic из внутреннего домена организации — конкретные рефереры не включаются, поскольку они, как правило, не находятся в открытом доступе.
  • Прямое общение между командой Pydantic и инженерами организации по вопросам использования Pydantic внутри организации.

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

{{ organisations }}


本文总阅读量