왜 Pydantic을 사용하나요?¶
오늘날 Pydantic은 한 달에 여러 번 다운로드되며 세계에서 가장 크고 가장 잘 알려진 조직에서 사용됩니다.
6년 전 Pydantic이 처음 출시된 이후 왜 그렇게 많은 사람들이 Pydantic을 채택했는지 알기는 어렵지만 여기에 몇 가지 추측이 있습니다.
스키마 유효성 검사를 지원하는 유형 힌트¶
Pydantic이 유효성을 검사하는 스키마는 일반적으로 Python 유형 힌트로 정의됩니다.
현대 Python을 작성하는 경우 유형 힌트를 사용하는 방법을 이미 알고 있기 때문에 유형 힌트가 유용합니다. 유형 힌트를 사용한다는 것은 Pydantic이 mypy 및 pyright와 같은 정적 입력 도구와 pycharm 및 vscode와 같은 IDE와 잘 통합된다는 것을 의미합니다.
???+ example "Example - just type hints" (This example requires Python 3.9+) ```py requires="3.9" from typing 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 "Learn more" See the documentation on supported types.
Performance¶
Pydantic's core validation logic is implemented in a separate package pydantic-core
, where validation for most types is implemented in Rust.
As a result, Pydantic is among the fastest data validation libraries for Python.
??? example "Performance Example - Pydantic vs. dedicated code" In general, dedicated code should be much faster than a general-purpose validator, but in this example Pydantic is >300% faster than dedicated code when parsing JSON and validating URLs.
```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
```
Unlike other performance-centric libraries written in compiled languages, Pydantic also has excellent support for customizing validation via functional validators.
!!! tip "Learn more" Samuel Colvin's talk at PyCon 2023 explains how pydantic-core
works and how it integrates with Pydantic.
Serialization¶
Pydantic provides functionality to serialize model in three ways:
- To a Python
dict
made up of the associated Python objects - To a Python
dict
made up only of "jsonable" types - To a JSON string
In all three modes, the output can be customized by excluding specific fields, excluding unset fields, excluding default values, and excluding None
values
??? example "Example - Serialization 3 ways" ```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"}
```
!!! tip "Learn more" See the documentation on serialization.
JSON Schema¶
JSON Schema can be generated for any Pydantic schema — allowing self-documenting APIs and integration with a wide variety of tools which support JSON Schema.
??? example "Example - JSON Schema" ```py from datetime import datetime
from pydantic import BaseModel 클래스 주소(BaseModel): 거리: str 도시: str 우편번호: str 클래스 회의(기본 모델): 언제: 날짜/시간 어디에: 주소 이유: str = '모름' 인쇄(Meeting.model_json_schema()) """ { ' $defs': { '주소': { '속성': { '거리': {'제목': '거리', '유형': '문자열'}, '도시': {'제목': '도시', '유형': '문자열'}, '우편번호': {'제목': '우편번호', '유형': '문자열'}, }, '필수': ['거리', '도시', '우편번호'], '제목': '주소', '유형': '개체', } }, '속성': { '언제': {'형식': '날짜-시간', '제목': '언제', '유형': '문자열'}, '어디': {'$ ref': '#/$defs/주소'}, '이유': {'기본값': '모름', '제목': '이유', '유형': '문자열'}, }, '필수': ['언제', '어디'], '제목': '회의', '유형': '개체', } """ ```
Pydantic generates JSON Schema version 2020-12, the latest version of the standard which is compatible with OpenAPI 3.1.
!!! Tip "자세히 알아보기" JSON 스키마 문서를 참조하세요.
엄격 모드 및 데이터 강제¶
기본적으로 Pydantic은 일반적으로 잘못된 유형을 허용하고 데이터를 올바른 유형으로 강제합니다. 예를 들어 int 필드에 전달된 숫자 문자열은 int로 구문 분석됩니다.
Pydantic에는 "엄격한 모드"라고도 알려진 strict=True 모드도 있습니다. 여기서 유형은 강제되지 않으며 입력 데이터가 스키마나 유형 힌트와 정확히 일치하지 않는 한 유효성 검사 오류가 발생합니다.
그러나 JSON에는 날짜/시간, UUID 또는 바이트와 같은 많은 일반적인 Python 유형과 일치하는 유형이 없기 때문에 JSON 데이터의 유효성을 검사할 때 엄격 모드는 꽤 쓸모가 없습니다.
이 문제를 해결하기 위해 Pydantic은 한 단계로 JSON을 구문 분석하고 검증할 수 있습니다. 이를 통해 RFC3339(ISO8601이라고도 함) 문자열과 같은 합리적인 데이터를 날짜/시간 객체로 변환할 수 있습니다. JSON 구문 분석은 Rust로 구현되므로 성능도 매우 좋습니다.
??? example "예 - 실제로 유용한 엄격한 모드" ``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'
```
!!! Tip "자세히 알아보기" 엄격 모드에 대한 문서를 참조하세요.
데이터 클래스, TypedDicts 등¶
Pydantic은 스키마를 생성하고 유효성 검사 및 직렬화를 수행하는 네 가지 방법을 제공합니다.
- BaseModel — 인스턴스 메소드를 통해 사용할 수 있는 많은 공통 유틸리티가 포함된 Pydantic의 자체 슈퍼 클래스입니다.
pydantic.dataclasses.dataclass
— 데이터 클래스가 초기화될 때 유효성 검사를 수행하는 표준 데이터 클래스 주위의 래퍼입니다.- TypeAdapter — 유효성 검사 및 직렬화를 위해 모든 유형을 조정하는 일반적인 방법입니다. 이를 통해 TypedDict 및 NamedTuple과 같은 유형은 물론 int 또는 timedelta와 같은 간단한 스칼라 값의 유효성을 검사할 수 있습니다. 지원되는 모든 유형은 TypeAdapter와 함께 사용할 수 있습니다.
- verify_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)
```
!!! tip "자세히 알아보기" 유효성 검사기, 사용자 정의 직렬 변환기 및 사용자 정의 유형에 대한 문서를 참조하세요.
생태계¶
이 글을 쓰는 시점에서 Pydantic에 의존하는 GitHub에는 214,100개의 저장소가 있고 PyPI에는 8,119개의 패키지가 있습니다.
Pydantic에 의존하는 일부 주목할만한 라이브러리:
Pydantic을 사용하는 더 많은 라이브러리는 Kludex/awesome-pydantic에서 찾을 수 있습니다.
Pydantic을 사용하는 조직¶
Pydantic을 사용하는 일부 주목할만한 회사 및 조직은 Pydantic을 사용하고 있는 이유/방법에 대한 의견을 함께 제공합니다.
아래 조직은 다음 기준 중 하나 이상과 일치하므로 포함되었습니다.
- 공개 저장소에서 pydantic을 종속성으로 사용
- 조직 내부 도메인에서 pydantic 문서 사이트로의 트래픽 참조 - 특정 참조자는 일반적으로 공개 도메인에 있지 않으므로 포함되지 않습니다.
- 조직 내에서 Pydantic 사용에 관해 Pydantic 팀과 조직에 고용된 엔지니어 간의 직접적인 의사소통
적절한 경우 이미 공개 도메인에 있는 몇 가지 추가 세부정보를 포함했습니다.
{{ organisations }}
本文总阅读量次