Waarom Pydantic gebruiken?¶
Tegenwoordig wordt Pydantic vele malen per maand gedownload en gebruikt door enkele van de grootste en meest herkenbare organisaties ter wereld.
Het is moeilijk te begrijpen waarom zoveel mensen Pydantic hebben geadopteerd sinds de oprichting zes jaar geleden, maar hier zijn een paar gissingen.
Typehints die schemavalidatie mogelijk maken¶
Het schema waartegen Pydantic valideert, wordt over het algemeen gedefinieerd door hints van het Python-type.
Typehints zijn hier geweldig voor, omdat je, als je moderne Python schrijft, al weet hoe je ze moet gebruiken. Het gebruik van typehints betekent ook dat Pydantic goed integreert met statische typetools zoals mypy en pyright en IDE's zoals pycharm en vscode.
???+ voorbeeld "Voorbeeld - typ gewoon hints" (Dit voorbeeld vereist Python 3.9+) ```py vereist = "3.9" door te typen 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.
!!! tip "Meer informatie" Zie de documentatie over ondersteunde typen.
Prestatie¶
De kernvalidatielogica van Pydantic is geïmplementeerd in een afzonderlijk pakket pydantic-core, waarbij validatie voor de meeste typen is geïmplementeerd in Rust.
Als gevolg hiervan behoort Pydantic tot de snelste gegevensvalidatiebibliotheken voor Python.
??? voorbeeld "Prestatievoorbeeld - Pydantic versus speciale code" Over het algemeen zou speciale code veel sneller moeten zijn dan een validator voor algemene doeleinden, maar in dit voorbeeld is Pydantic >300% sneller dan speciale code bij het parseren van JSON en het valideren van URL's.
```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
```
In tegenstelling tot andere prestatiegerichte bibliotheken die in gecompileerde talen zijn geschreven, heeft Pydantic ook uitstekende ondersteuning voor het aanpassen van validatie via functionele validators.
!!! tip "Meer informatie" Samuel Colvin's lezing op PyCon 2023 legt uit hoe pydantic-core werkt en hoe het integreert met Pydantic.
Serialisatie¶
Pydantic biedt functionaliteit om modellen op drie manieren te serialiseren:
- Naar een Python-dictaat dat bestaat uit de bijbehorende Python-objecten
- Naar een Python-dictaat dat alleen uit "jsonable" -typen bestaat
- Naar een JSON-tekenreeks
In alle drie de modi kan de uitvoer worden aangepast door specifieke velden uit te sluiten, niet-ingestelde velden uit te sluiten, standaardwaarden uit te sluiten en Geen-waarden uit te sluiten
??? voorbeeld "Voorbeeld - Serialisatie op 3 manieren" ```py uit 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"}
```
!!! tip "Meer informatie" Zie de documentatie over serialisatie.
JSON-schema¶
JSON Schema kan voor elk Pydantic-schema worden gegenereerd, waardoor zelfdocumenterende API's en integratie mogelijk zijn met een grote verscheidenheid aan tools die JSON Schema ondersteunen.
??? voorbeeld "Voorbeeld - JSON Schema" ```py van datetime import datetime
from pydantic import BaseModel klasse Adres (basismodel): straat: str stad: str postcode: str klasvergadering (basismodel): wanneer: datumtijd waar: Adres waarom: str = 'Geen idee' print(Vergadering.model_json_schema()) """ { ' $defs': { 'Adres': { 'eigenschappen': { 'straat': {'title': 'Straat', 'type': 'string'}, 'stad': {'title': 'Stad', 'type': 'string'}, 'postcode': {'title': 'Postcode', 'type': 'string'}, }, 'vereist': ['straat', 'plaats', 'postcode'], 'titel': 'Adres', 'type': 'object', } }, 'eigenschappen': { 'wanneer': {'format': 'datum-tijd', 'title': 'Wanneer', 'type': 'string'}, 'waar': {'$ ref': '#/$defs/Adres'}, 'waarom': {'default': 'Geen idee', 'title': 'Waarom', 'type': 'string'}, }, 'vereist': ['wanneer', 'waar'], 'titel': 'Vergadering', 'type': 'object', } """ ```
Pydantic genereert JSON Schema versie 2020-12, de nieuwste versie van de standaard die compatibel is met OpenAPI 3.1.
!!! tip "Meer informatie" Zie de documentatie over JSON Schema.
Strikte modus en datadwang¶
Standaard is Pydantic tolerant ten opzichte van veelvoorkomende onjuiste typen en dwingt gegevens naar het juiste type - bijvoorbeeld een numerieke reeks die aan een int-veld wordt doorgegeven, wordt geparseerd als een int.
Pydantic heeft ook de strict=True-modus – ook bekend als de ‘Strict-modus’ – waarbij typen niet worden afgedwongen en er een validatiefout optreedt, tenzij de invoergegevens exact overeenkomen met het schema of de typehint.
Maar de strikte modus zou behoorlijk nutteloos zijn bij het valideren van JSON-gegevens, omdat JSON geen typen heeft die overeenkomen met veel voorkomende Python-typen zoals datetime, UUID of bytes.
Om dit op te lossen kan Pydantic JSON in één stap parseren en valideren. Dit maakt verstandige gegevensconversie mogelijk, zoals RFC3339-reeksen (ook bekend als ISO8601) naar datetime-objecten. Omdat de JSON-parsering in Rust is geïmplementeerd, is deze ook zeer performant.
??? voorbeeld "Voorbeeld - Strikte modus die eigenlijk nuttig is" ```py van 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'
```
!!! tip "Meer informatie" Zie de documentatie over de strikte modus.
Dataklassen, TypedDicts en meer¶
Pydantic biedt vier manieren om schema's te maken en validatie en serialisatie uit te voeren:
- BaseModel — Pydantic's eigen superklasse met veel algemene hulpprogramma's beschikbaar via instancemethoden.
pydantic.dataclasses.dataclass
— een wrapper rond standaarddataklassen die validatie uitvoert wanneer een dataklasse wordt geïnitialiseerd.- TypeAdapter — een algemene manier om elk type aan te passen voor validatie en serialisatie. Hierdoor kunnen typen als TypedDict en NamedTuple worden gevalideerd, evenals eenvoudige scalaire waarden zoals int of timedelta. Alle ondersteunde typen kunnen worden gebruikt met TypeAdapter.
- validate_call — een decorateur die validatie uitvoert bij het aanroepen van een functie.
??? voorbeeld "Voorbeeld - schema gebaseerd op TypedDict" ```py uit 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
Maatwerk¶
Functionele validators en serializers, evenals een krachtig protocol voor aangepaste typen, zorgen ervoor dat de manier waarop Pydantic werkt, per veld of per type kan worden aangepast.
??? voorbeeld "Aanpassingsvoorbeeld - wrap validators" "wrap validators" zijn nieuw in Pydantic V2 en zijn een van de krachtigste manieren om Pydantic-validatie aan te passen. ```py uit datetime importeer datetime, tijdzone
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)
```
!!! tip "Meer informatie" Zie de documentatie over validators, aangepaste serializers en aangepaste typen.
Ecosysteem¶
Op het moment van schrijven zijn er 214.100 repositories op GitHub en 8.119 pakketten op PyPI die afhankelijk zijn van Pydantic.
Enkele opmerkelijke bibliotheken die afhankelijk zijn van Pydantic:
Meer bibliotheken die Pydantic gebruiken, zijn te vinden op Kludex/awesome-pydantic.
Organisaties die Pydantic gebruiken¶
Enkele opmerkelijke bedrijven en organisaties die Pydantic gebruiken, samen met commentaar over waarom/hoe we weten dat ze Pydantic gebruiken.
Onderstaande organisaties zijn opgenomen omdat zij voldoen aan één of meer van de volgende criteria:
- Pydantic gebruiken als afhankelijkheid in een openbare repository
- Verkeer doorverwijzen naar de pydantic-documentatiesite vanuit een intern domein van de organisatie: specifieke verwijzingen zijn niet opgenomen omdat deze zich doorgaans niet in het publieke domein bevinden
- Directe communicatie tussen het Pydantic-team en engineers in dienst van de organisatie over het gebruik van Pydantic binnen de organisatie
We hebben waar nodig wat extra details toegevoegd die al openbaar zijn.
{{ organisations }}
本文总阅读量次