为何使用 Pydantic 验证?¶
如今,Pydantic 每月的下载量高达数百万次,并被世界上一些规模最大、最知名的组织所使用。
很难确切知道为何自六年前诞生以来,有如此多的人采用 Pydantic,但这里有几个猜测。
类型提示驱动模式验证¶
Pydantic 用于验证的模式通常由 Python 的类型提示定义。
类型提示非常适合此目的,因为如果你正在编写现代 Python 代码,你已经知道如何使用它们。使用类型提示也意味着 Pydantic 能与静态类型检查工具(如 mypy 和 Pyright)以及集成开发环境(IDE,如 PyCharm 和 VSCode)很好地集成。
示例 - 仅使用类型提示
from typing import Annotated, Literal
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)]}
name
字段仅用str
注解——任何字符串都是允许的。Literal
类型用于强制color
字段的值必须是'red'
或'green'
。- 即使我们想应用 Python 类型本身无法封装的约束,我们也可以使用
Annotated
和annotated-types
来强制约束,同时仍然保持类型支持。 - 我并不是说“bazam”真的是水果的一个属性,而是为了展示可以轻松验证任意复杂的类型。
了解更多
请参阅关于支持的类型的文档。
性能¶
Pydantic 的核心验证逻辑在一个独立的包(pydantic-core
)中实现,其中大多数类型的验证逻辑是用 Rust 实现的。
因此,Pydantic 是 Python 中最快的数据验证库之一。
性能示例 - Pydantic vs. 专用代码
通常,专用代码应该比通用验证器快得多,但在这个例子中,当解析 JSON 和验证 URL 时,Pydantic 比专用代码快了超过 300%。
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 还通过函数验证器为自定义验证提供了出色的支持。
了解更多
Samuel Colvin 在 PyCon 2023 上的演讲解释了 pydantic-core
的工作原理及其与 Pydantic 的集成方式。
序列化¶
Pydantic 提供了三种序列化模型的方式:
- 序列化为由相应 Python 对象组成的 Python
dict
。 - 序列化为仅由“可 JSON 化”类型组成的 Python
dict
。 - 序列化为 JSON 字符串。
在这三种模式下,都可以通过排除特定字段、排除未设置的字段、排除默认值以及排除 None
值来自定义输出。
示例 - 三种序列化方式
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 Schema¶
可以为任何 Pydantic 模式生成 JSON Schema——这使得 API 能够自我文档化,并能与支持 JSON Schema 格式的各种工具集成。
示例 - JSON Schema
from datetime import datetime
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
zipcode: str
class Meeting(BaseModel):
when: datetime
where: Address
why: str = 'No idea'
print(Meeting.model_json_schema())
"""
{
'$defs': {
'Address': {
'properties': {
'street': {'title': 'Street', 'type': 'string'},
'city': {'title': 'City', 'type': 'string'},
'zipcode': {'title': 'Zipcode', 'type': 'string'},
},
'required': ['street', 'city', 'zipcode'],
'title': 'Address',
'type': 'object',
}
},
'properties': {
'when': {'format': 'date-time', 'title': 'When', 'type': 'string'},
'where': {'$ref': '#/$defs/Address'},
'why': {'default': 'No idea', 'title': 'Why', 'type': 'string'},
},
'required': ['when', 'where'],
'title': 'Meeting',
'type': 'object',
}
"""
Pydantic 兼容最新版本的 JSON Schema 规范(2020-12),该版本与 OpenAPI 3.1 兼容。
了解更多
严格模式和数据转换¶
默认情况下,Pydantic 对常见的错误类型持宽容态度,并将数据转换为正确的类型——例如,传递给 int
字段的数字字符串将被解析为 int
。
Pydantic 还有一个严格模式,在这种模式下,类型不会被转换,除非输入数据与预期的模式完全匹配,否则会引发验证错误。
但在验证 JSON 数据时,严格模式可能没什么用,因为 JSON 没有与许多常见 Python 类型(如 datetime
、UUID
或 bytes
)匹配的类型。
为了解决这个问题,Pydantic 可以在一个步骤中解析和验证 JSON。这允许进行合理的数据转换(例如,将字符串解析为 datetime
对象)。由于 JSON 解析是在 Rust 中实现的,所以它的性能也非常高。
示例 - 真正有用的严格模式
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'
了解更多
请参阅关于严格模式的文档。
数据类、类型化字典等¶
Pydantic 提供了四种创建模式以及执行验证和序列化的方法:
BaseModel
— Pydantic 自己的超类,通过实例方法提供了许多常用工具。- Pydantic 数据类 — 对标准数据类的封装,并执行额外的验证。
TypeAdapter
— 一种通用的方法,用于适配任何类型以进行验证和序列化。这使得像TypedDict
和NamedTuple
这样的类型以及简单类型(如int
或timedelta
)都能够被验证 — 所有支持的类型都可以与TypeAdapter
一起使用。validate_call
— 一个装饰器,用于在调用函数时执行验证。
示例 - 基于 TypedDict
的模式
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',
}
"""
- 用于
TypedDict
的TypeAdapter
执行验证,它也可以直接使用validate_json
验证 JSON 数据。 - 使用
dump_python
将TypedDict
序列化为 Python 对象,也可以使用dump_json
序列化为 JSON。 TypeAdapter
也可以生成 JSON Schema。
自定义¶
函数式验证器和序列化器,以及一个强大的自定义类型协议,意味着 Pydantic 的操作方式可以基于每个字段或每种类型进行自定义。
自定义示例 - 包装验证器
“包装验证器”是 Pydantic V2 中的新功能,是自定义验证最强大的方式之一。
from datetime import datetime, timezone
from typing import Any
from pydantic_core.core_schema import ValidatorFunctionWrapHandler
from pydantic import BaseModel, field_validator
class Meeting(BaseModel):
when: datetime
@field_validator('when', mode='wrap')
def when_now(
cls, input_value: Any, handler: ValidatorFunctionWrapHandler
) -> datetime:
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(3600))
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 上有 466,400 个代码仓库和 PyPI 上有 8,119 个包依赖于 Pydantic。
一些依赖于 Pydantic 的知名库:
huggingface/transformers
138,570 星hwchase17/langchain
99,542 星tiangolo/fastapi
80,497 星apache/airflow
38,577 星lm-sys/FastChat
37,650 星microsoft/DeepSpeed
36,521 星OpenBB-finance/OpenBBTerminal
35,971 星gradio-app/gradio
35,740 星ray-project/ray
35,176 星pola-rs/polars
31,698 星Lightning-AI/lightning
28,902 星mindsdb/mindsdb
27,141 星embedchain/embedchain
24,379 星pynecone-io/reflex
21,558 星heartexlabs/label-studio
20,571 星Sanster/lama-cleaner
20,313 星mlflow/mlflow
19,393 星RasaHQ/rasa
19,337 星spotDL/spotify-downloader
18,604 星chroma-core/chroma
17,393 星airbytehq/airbyte
17,120 星openai/evals
15,437 星tiangolo/sqlmodel
15,127 星ydataai/ydata-profiling
12,687 星pyodide/pyodide
12,653 星dagster-io/dagster
12,440 星PaddlePaddle/PaddleNLP
12,312 星matrix-org/synapse
11,857 星lucidrains/DALLE2-pytorch
11,207 星great-expectations/great_expectations
10,164 星modin-project/modin
10,002 星aws/serverless-application-model
9,402 星sqlfluff/sqlfluff
8,535 星replicate/cog
8,344 星autogluon/autogluon
8,326 星lucidrains/imagen-pytorch
8,164 星brycedrennan/imaginAIry
8,050 星vitalik/django-ninja
7,685 星NVlabs/SPADE
7,632 星bridgecrewio/checkov
7,340 星bentoml/BentoML
7,322 星skypilot-org/skypilot
7,113 星apache/iceberg
6,853 星deeppavlov/DeepPavlov
6,777 星PrefectHQ/marvin
5,454 星NVIDIA/NeMo-Guardrails
4,383 星microsoft/FLAML
4,035 星jina-ai/discoart
3,846 星docarray/docarray
3,007 星aws-powertools/powertools-lambda-python
2,980 星roman-right/beanie
2,172 星art049/odmantic
1,096 星
更多使用 Pydantic 的库可以在 Kludex/awesome-pydantic
找到。
使用 Pydantic 的组织¶
一些使用 Pydantic 的知名公司和组织,以及关于我们为何/如何知道他们在使用 Pydantic 的评论。
以下组织被列入是因为它们符合以下一个或多个标准:
- 在其公共代码仓库中将 Pydantic 作为依赖项。
- 从组织内部域名向 Pydantic 文档网站引荐流量——具体的引荐来源未包含在内,因为它们通常不属于公共领域。
- Pydantic 团队与该组织雇佣的工程师就组织内部使用 Pydantic 的情况进行直接沟通。
在适当且信息已在公共领域的情况下,我们提供了一些额外的细节。
Adobe¶
adobe/dy-sql
使用 Pydantic。
亚马逊和 AWS¶
- powertools-lambda-python
- awslabs/gluonts
- AWS 在 2022 年赞助了 Samuel Colvin 5,000 美元用于 Pydantic 的开发工作。
Anthropic¶
anthropics/anthropic-sdk-python
使用 Pydantic。
Apple (苹果)¶
(基于上述标准)
ASML (阿斯麦)¶
(基于上述标准)
AstraZeneca (阿斯利康)¶
AstraZeneca
GitHub 组织中的多个仓库依赖于 Pydantic。
Cisco Systems (思科系统)¶
- 在其RADKit 中使用的开源软件报告中列出了 Pydantic。
cisco/webex-assistant-sdk
Comcast (康卡斯特)¶
(基于上述标准)
Datadog¶
- 在
DataDog/integrations-core
和其他仓库中广泛使用 Pydantic。 - 与 Datadog 的工程师就他们如何使用 Pydantic 进行了沟通。
Facebook¶
facebookresearch
GitHub 组织中的多个仓库依赖于 Pydantic。
GitHub¶
GitHub 在 2022 年赞助了 Pydantic 750 美元。
Google (谷歌)¶
在 google/turbinia
和其他仓库中广泛使用 Pydantic。
HSBC (汇丰银行)¶
(基于上述标准)
IBM¶
IBM
GitHub 组织中的多个仓库依赖于 Pydantic。
Intel (英特尔)¶
(基于上述标准)
Intuit¶
(基于上述标准)
Intergovernmental Panel on Climate Change (政府间气候变化专门委员会)¶
解释 IPCC 如何使用 Pydantic 的推文。
JPMorgan (摩根大通)¶
(基于上述标准)
Jupyter¶
Microsoft (微软)¶
- DeepSpeed 深度学习优化库广泛使用 Pydantic。
microsoft
GitHub 组织中的多个仓库依赖于 Pydantic,特别是他们的- Pydantic 也在
Azure
GitHub 组织中被使用。 - GitHub 上的评论显示,微软工程师在 Windows 和 Office 中使用 Pydantic。
Molecular Science Software Institute (分子科学软件研究所)¶
MolSSI
GitHub 组织中的多个仓库依赖于 Pydantic。
NASA (美国国家航空航天局)¶
NASA
GitHub 组织中的多个仓库依赖于 Pydantic。
NASA 还在其 JWST 项目中通过 FastAPI 使用 Pydantic 来处理来自詹姆斯·韦伯空间望远镜的图像,详见这条推文。
Netflix (奈飞)¶
Netflix
GitHub 组织中的多个仓库依赖于 Pydantic。
NSA (美国国家安全局)¶
nsacyber/WALKOFF
仓库依赖于 Pydantic。
NVIDIA (英伟达)¶
NVIDIA
GitHub 组织中的多个仓库依赖于 Pydantic。
根据他们的文档,其“Omniverse 服务”依赖于 Pydantic。
OpenAI¶
根据 GitHub 上的这个讨论,OpenAI 在其 ChatCompletions API 中使用 Pydantic。
据传,OpenAI 在其内部服务中广泛使用 Pydantic。
Oracle (甲骨文)¶
(基于上述标准)
Palantir¶
(基于上述标准)
Qualcomm (高通)¶
(基于上述标准)
Red Hat (红帽)¶
(基于上述标准)
Revolut¶
据传,Revolut 的所有内部服务都使用 FastAPI 构建,因此也使用了 Pydantic。
Robusta¶
robusta-dev/robusta
仓库依赖于 Pydantic。
Salesforce¶
Salesforce 在 2022 年赞助了 Samuel Colvin 10,000 美元用于 Pydantic 的开发工作。
Starbucks (星巴克)¶
(基于上述标准)
Texas Instruments (德州仪器)¶
(基于上述标准)
Twilio¶
(基于上述标准)
Twitter¶
Twitter 的 the-algorithm
仓库,即他们开源的推荐引擎,使用了 Pydantic。
UK Home Office (英国内政部)¶
(基于上述标准)