Зачем использовать Пидантик?¶
Сегодня 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 предоставляет функциональность для сериализации модели тремя способами:
- К словарю Python, состоящему из связанных объектов Python
- Для словаря Python, состоящего только из типов «jsonable»
- К строке 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 предоставляет четыре способа создания схем, а также выполнения проверки и сериализации:
- BaseModel — собственный суперкласс Pydantic со множеством общих утилит, доступных через методы экземпляра.
pydantic.dataclasses.dataclass
— оболочка стандартных классов данных, которая выполняет проверку при инициализации класса данных.- TypeAdapter — общий способ адаптации любого типа для проверки и сериализации. Это позволяет проверять такие типы, как TypedDict и NamedTuple, а также простые скалярные значения, такие как int или timedelta — все поддерживаемые типы можно использовать с TypeAdapter.
- 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 }}
本文总阅读量次