一、什么是 Pydantic?
Pydantic 是 Python 中使用最广泛的数据验证库,核心思路是:用类型注解(type hints)定义数据应该长什么样,然后自动完成验证、解析和序列化。
- 验证核心由 Rust 编写(pydantic-core),性能极快
- 每月下载量超过 5.5 亿次,PyPI 上约 8000 个包依赖它(FastAPI、Hugging Face、SQLModel、LangChain 等)
- 无需手写任何验证逻辑,兼容 IDE 和静态分析工具
一句话总结:Pydantic 帮你”管数据、查错、自动转格式”,靠 Python 自带的类型注解干活。
二、安装
pip install pydantic
若需配置管理能力,额外安装:
pip install pydantic-settings
三、核心概念:BaseModel
模型(Model)就是继承 BaseModel 的类,字段用类型注解声明:
from datetime import datetime
from pydantic import BaseModel, PositiveInt
class User(BaseModel):
id: int # 必填字段
name: str = 'John Doe' # 有默认值,可选
signup_ts: datetime | None # 可为 None 的 datetime
tastes: dict[str, PositiveInt] # 嵌套类型:dict[str, int] 且值 > 0
# 传入"脏数据"(字符串、bytes、非标准格式)
external_data = {
'id': '123', # 字符串 → 自动转 int
'signup_ts': '2019-06-01 12:22', # ISO 字符串 → 自动转 datetime
'tastes': {'wine': 9, b'cheese': 7, 'cabbage': '1'},
}
user = User(**external_data)
print(user.id) # 123
print(user.model_dump())
# {'id': 123, 'name': 'John Doe', 'signup_ts': datetime(...),
# 'tastes': {'wine': 9, 'cheese': 7, 'cabbage': 1}}
关键点:
- 初始化即验证,验证通过才生成实例
- 宽松模式(lax)下会自动强制类型转换(
'123'→123,b'cheese'→'cheese'),这是刻意的设计 - 验证失败抛
ValidationError,一次性包含所有错误
四、模型的核心方法
| 方法 | 作用 |
|---|---|
model_validate(obj) | 验证任意对象(dict / 模型实例 / ORM 对象) |
model_validate_json(str) | 直接验证 JSON 字符串(比先解析再验证更快) |
model_validate_strings(dict) | 从全字符串 dict 验证(如表单数据) |
model_construct() | 跳过验证直接建模型(性能场景) |
model_dump() / model_dump_json() | 序列化为 dict / JSON 字符串 |
model_copy() | 复制模型(支持 deep=True) |
model_json_schema() | 生成 JSON Schema,可与其他工具对接 |
model_rebuild() | 重建 schema(前向引用/递归模型时用) |
model_fields / model_fields_set | 字段定义 / 本次显式传入的字段集合 |
五、字段定制:Field()
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(min_length=1, max_length=50, description="商品名")
price: float = Field(gt=0, le=10000) # 范围约束
tags: list[str] = Field(default_factory=list) # 可变默认值必须用 factory
alias: str = Field(alias="SKU") # 输入别名
常用约束:gt/ge/lt/le、min_length/max_length、pattern、default_factory、alias、exclude、frozen 等。
六、自定义验证器(Validators)
1. 字段级验证器 — 四种模式
from pydantic import BaseModel, field_validator
class Model(BaseModel):
number: int
@field_validator('number', mode='after') # after 是默认模式
@classmethod
def is_even(cls, value: int) -> int:
if value % 2 == 1:
raise ValueError(f'{value} is not an even number')
return value # 必须返回验证后的值
| 模式 | 时机 |
|---|---|
before | Pydantic 内部解析之前,处理原始输入(可能是任意类型) |
after | 内部验证之后,类型安全,最常用 |
plain | 立即终止验证,不经过 Pydantic 内部校验 |
wrap | 最灵活:包住内部验证,可 try/except、截断、提前返回 |
也可以用 Annotated 模式定义可复用的验证器:
from typing import Annotated
from pydantic import AfterValidator
def is_even(value: int) -> int:
if value % 2 == 1:
raise ValueError(f'{value} is not an even number')
return value
EvenNumber = Annotated[int, AfterValidator(is_even)]
class Model(BaseModel):
my_number: EvenNumber # 可复用到任意模型
evens: list[EvenNumber] # 甚至作用于列表元素
2. 模型级验证器(跨字段校验)
from typing_extensions import Self
from pydantic import BaseModel, model_validator
class UserModel(BaseModel):
username: str
password: str
password_repeat: str
@model_validator(mode='after')
def check_passwords_match(self) -> Self:
if self.password != self.password_repeat:
raise ValueError('Passwords do not match')
return self
3. 验证上下文 ValidationInfo
验证器可接收 info: ValidationInfo,访问已验证的兄弟字段(info.data)和调用方传入的上下文(info.context)——常用于”某字段是否依赖另一字段""根据环境决定规则”等场景。
七、配置:ConfigDict
from pydantic import BaseModel, ConfigDict
class Model(BaseModel):
model_config = ConfigDict(
extra='forbid', # 'ignore'忽略 | 'forbid'禁止 | 'allow'存到__pydantic_extra__
strict=True, # 严格模式:不做类型转换
frozen=True, # 不可变模型
str_max_length=10,
from_attributes=True, # 允许从任意对象读取属性(ORM 集成)
validate_assignment=True, # 赋值时也校验
)
严格模式(strict):默认宽松模式会尽力转换类型并可能丢失信息(如 3.000→3);开启 strict 后类型必须完全匹配。
八、嵌套模型与泛型模型
class Foo(BaseModel):
count: int
class Spam(BaseModel):
foo: Foo # 嵌套:dict 自动转成 Foo 实例
bars: list[Foo] # list 中的每个 dict 元素也会被自动转成 Foo
泛型模型——复用通用结构(如统一响应包装):
from typing import Generic, TypeVar
from pydantic import BaseModel
T = TypeVar('T') # 类型变量,用于声明泛型参数
class Response(BaseModel, Generic[T]):
data: T # 泛型序列化:实例化时指定具体类型来决定 data 的类型
# 用具体类型参数化泛型模型
print(Response[int](data=1)) # data=1(int 类型)
print(Response[list[str]](data=['a'])) # data=['a'](list[str] 类型)
泛型模型的核心价值:同一套响应包装结构可复用到任意数据类型,同时在实例化时获得完整的类型检查与校验。
九、错误处理
from pydantic import BaseModel, ValidationError
class Model(BaseModel):
list_of_ints: list[int]
a_float: float
try:
Model(**{'list_of_ints': ['1', 2, 'bad'], 'a_float': 'not a float'})
except ValidationError as e:
print(e.errors())
# 一次抛出所有错误,每条含:type、loc、msg、input、url
# 例如 [{'type': 'int_parsing', 'loc': ('list_of_ints', 2),
# 'msg': 'Input should be a valid integer...', 'input': 'bad'}]
注意:一次验证会收集所有字段错误(list_of_ints 索引 2 的 'bad' 和 a_float 的 'not a float'),而不是在第一个错误处就停止。
十、其他重要能力
- Pydantic Settings:
pydantic-settings支持从环境变量/.env文件读取配置校验 - TypeAdapter:对单个类型(非模型)做验证/序列化,如
TypeAdapter(list[int]).validate_json(...) - Dataclasses / TypedDict:原生 dataclass 和 TypedDict 也能获得验证能力
- JSON Schema 生成:
model_json_schema()一键对接 OpenAPI 等工具 - ORM 集成:
from_attributes=True后可直接model_validate()SQLAlchemy 对象
十一、v2 与 v1 的主要差异
| v1 | v2 | 说明 |
|---|---|---|
parse_obj() / parse_raw() | model_validate() / model_validate_json() | v1 的解析方法在 v2 中被 model_validate 系列方法取代 |
.dict() / .json() | model_dump() / model_dump_json() | v2 序列化接口统一使用 model_dump 系列 |
@validator / @root_validator | @field_validator / @model_validator | 含 before/after/wrap 模式 |
Config 内部类 | model_config = ConfigDict(...) | 配置改为类属性 model_config 声明 |
update_forward_refs() | model_rebuild() | 前向引用/递归模型的 schema 重建 |
| 纯 Python 实现 | Rust 核心(pydantic-core) | 性能提升 5-50 倍 |
__fields__ | model_fields | 字段元数据访问属性改名 |
十二、在一个 FastAPI + vLLM 网关中的实际应用
用真实项目(llm-service——一个 FastAPI + vLLM 的 LLM 业务网关)看 Pydantic 的落地方式。Pydantic 用在两处:请求/响应契约(schemas.py)和配置管理(pydantic-settings)。
1. 请求/响应契约 — schemas.py
from pydantic import BaseModel, Field
from typing import Literal
class Message(BaseModel): # 单条对话消息
role: Literal["system", "user", "assistant"] # 枚举约束:非法 role 直接 422
content: str
class ChatRequest(BaseModel): # 请求体
messages: list[Message] = Field(..., min_length=1) # 嵌套模型 + 非空校验
temperature: float = Field(default=0.7, ge=0.0, le=2.0) # 数值范围约束
top_p: float = Field(default=1.0, ge=0.0, le=1.0)
max_tokens: int = Field(default=2048, ge=1, le=4096) # 防烧钱上限
stream: bool = False
user_id: str = ""
class Usage(BaseModel): # 嵌套响应对象
prompt_tokens: int
completion_tokens: int
total_tokens: int
class ChatResponse(BaseModel): # 响应体
id: str
reply: str
usage: Usage
latency_ms: int
from_cache: bool = False
用到的特性:
Field(..., min_length=1)强制 messages 非空ge/le数值范围约束(temperature 0~2、max_tokens 上限 4096)Literal类型 → role 只能是 system/user/assistant,非法值自动 422- 嵌套模型
list[Message]:前端传 dict 数组,Pydantic 自动转成Message实例 - 默认值:
stream=False、from_cache=False等
2. 在路由中如何被使用 — chat.py
@router.post("/v1/chat/completions")
async def chat(req: ChatRequest, request: Request, tenant: str = Depends(authenticate)):
...
- FastAPI 自动注入:
req: ChatRequest作参数 → 请求体自动解析、校验,非法输入返回 422(含 Pydantic 错误详情),无需手写任何 if/else - 读字段:
req.messages、req.temperature、req.stream、req.max_tokens - 模型转 dict:
{"role": m.role, "content": m.content} for m in req.messages(直接属性访问) - 构造响应模型:缓存命中时
ChatResponse(id=f"c-{trace_id}", reply=cached, usage=Usage(prompt_tokens=0, ...));正常路径usage=Usage(**usage)—— 用**解包 dict 构造嵌套模型 - FastAPI 返回
ChatResponse时自动序列化为 JSON 响应
3. 配置管理 — config.py(pydantic-settings)
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_prefix="LLM_")
vllm_base_url: str = "http://vllm:8000/v1"
max_concurrency: int = 10
api_keys: str = "sk-test-1111:tenant-a,sk-demo-2222:tenant-b"
tenant_concurrency: dict = {"tenant-a": 5, "tenant-b": 5}
cache_enabled: bool = True
settings = Settings() # 模块级单例
用到的特性:
env_file=".env"+env_prefix="LLM_":环境变量LLM_VLLM_BASE_URL自动覆盖默认值,类型自动转换(str→int/bool/dict)- dict 类型字段
tenant_concurrency:环境变量传 JSON 字符串也能自动解析 - 全局单例
settings,被cache.py、llm_client.py、concurrency.py、auth.py到处引用
4. 项目依赖版本
pydantic==2.9.2
pydantic-settings==2.6.1
5. 项目实践小结
| 用法 | 位置 | 效果 |
|---|---|---|
| 请求体校验 + 嵌套模型 | schemas.py + chat.py | 非法请求自动 422,零手写校验 |
| 字段约束 | Field(ge/le/min_length) | 数值范围、非空约束 |
| 响应模型 | schemas.py | 结构化返回 + FastAPI 自动序列化 |
| 配置管理 | config.py | 环境变量驱动,类型安全 |
需要特别强调的是:这个项目没有用到任何进阶特性——没有自定义验证器(field_validator/model_validator)、没有 strict 严格模式、没有主动调用 model_dump(序列化完全交由 FastAPI 自动完成)。项目的 Pydantic 用法停留在”基本款”:仅依赖 FastAPI 内置集成即可完成全部工作。这恰恰印证了一个关键结论——Pydantic 的价值在于渐进式引入:最简场景下只需声明类型注解 + Field 约束就能获得可靠的输入校验;当业务出现跨字段校验、自定义解析规则等复杂需求时,再按需引入验证器、严格模式等高级能力。不必一开始就铺满全部特性。
十三、生态与典型应用
- FastAPI:请求体、响应体、查询参数全部基于 Pydantic,自动生成 OpenAPI 文档
- LangChain / Pydantic AI:结构化输出、工具调用的参数校验
- SQLModel:SQLAlchemy + Pydantic 结合,ORM 与验证一体
- 配置管理:pydantic-settings 读环境变量并校验类型
参考资料(已联网核实的最新官方文档):
- 官方文档(v2.13):https://pydantic.dev/docs/validation/latest/get-started/
- 模型详解:https://docs.pydantic.dev/latest/concepts/models/
- 验证器详解:https://docs.pydantic.dev/latest/concepts/validators/
- 中文文档:https://pydantic.com.cn/