Pydantic v2入门教程:模型字段与验证器详解
Pydanticv2通过BaseModel定义模型,利用类型注解声明字段,Field()添加约束,支持Annotated复用约束。宽松模式自动转换类型,严格模式拒绝隐式转换。field_validator与model_validator实现自定义验证,ValidationInfo传递上下文。field_serializer控制输出格式,嵌套模型自动递归校验。
本文全面解析 Pydantic v2 中 API 的每个核心模块:如何定义模型、为字段添加约束、编写验证器、组合嵌套结构、控制序列化行为,以及最后生成 JSON Schema。所有示例均基于 Pydantic v2 和 Python 3.10,每个代码清单都完整可运行,可直接复制测试。
使用 BaseModel 定义模型
Pydantic 的基石是 BaseModel。继承它后,只需用类型注解声明字段——创建类时 Pydantic 会自动检查注解、构建校验 schema,后续每次实例化都会依据此 schema 验证输入数据。
没有默认值的字段为必填项;有默认值或声明为 T | None 且默认 None 的字段为可选项。看一个最简单的例子:
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
country: str = "US" # 可选,默认 "US"
apartment: str | None = None # 可选,默认 None
addr = Address(
street="123 Main St",
city="Springfield",
state="IL",
zip_code="62704",
)
print(addr)
# street='123 Main St' city='Springfield' state='IL' zip_code='62704' country='US' apartment=None
如果仅靠注解加默认值不够用,可以借助 Field()。它能为字段附加元数据、约束和文档说明,让模型更规范。
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(
min_length=1, max_length=200,
title="Product Name",
description="商品显示名称",
examples=["Widget Pro"]
)
sku: str = Field(
pattern=r"^[A-Z]{2,4}-\d{4,8}$",
description="库存单位,格式 'XX-0000'",
examples=["WP-12345"]
)
price: float = Field(
gt=0, le=999_999.99,
description="美元价格,必须为正"
)
quantity: int = Field(
default=0, ge=0,
description="库存数量,不可为负"
)
category: str = Field(
validation_alias="product_category",
description="来自目录系统的产品类别"
)
product = Product(
name="Widget Pro", sku="WP-12345", price=29.99,
quantity=150, product_category="Electronics"
)
print(product.category) # Electronics
利用 Annotated 复用约束:如果需要在多个地方重复使用相同的约束条件(例如“正整数”或“不超过100个字符的字符串”),可以用 Annotated 将其定义为类型别名,然后直接复用。效果与 Field() 一致,但更便于跨模型共享。
from typing import Annotated
from pydantic import BaseModel, Field
PositiveInt = Annotated[int, Field(gt=0)]
ShortStr = Annotated[str, Field(min_length=1, max_length=100)]
class Widget(BaseModel):
quantity: PositiveInt
name: ShortStr
两种风格在校验行为上等价,但如果需要让类型跨多个模型复用,强烈推荐 Annotated。
类型强制转换与严格模式:默认情况下 Pydantic 采用宽松模式——兼容类型会自动转换而不会直接拒绝。这在处理 JSON 数据(全部为字符串)时非常方便。例如:
event = Event(name="PyCon", attendees="500", event_date="2025-05-15")
# "500" 自动转 int,"2025-05-15" 自动转 date
如果想启用严格模式(拒绝任何隐式转换),可以在模型级设置 model_config = ConfigDict(strict=True),或在字段级添加 Field(strict=True) 或 Annotated[int, Strict()]。一般来说,数据源已经是强类型(如内部 Python 调用、强类型数据库驱动)时使用严格模式更安全;解析 JSON 或表单数据时建议保持宽松。
验证器:field_validator 与 model_validator
Pydantic 内置的类型系统和 Field() 约束已覆盖大部分校验需求。但如果需要更灵活的自定义逻辑,可以使用自定义验证器。
@field_validator 有四种模式:
mode='after'(默认):Pydantic 内置校验执行完毕后运行,接收已解析且带类型的值。mode='before':在内置校验之前运行,接收原始输入(可能是字符串、dict 等)。mode='wrap':包裹内置校验,可用于日志记录或错误转译。mode='plain':完全替代内置校验,自行接管全部校验逻辑。
class User(BaseModel):
username: str = Field(min_length=3, max_length=30)
email: str
@field_validator("username", mode="before")
@classmethod
def normalize_username(cls, v: object) -> str:
if not isinstance(v, str):
raise ValueError("Username must be a string")
return v.strip().lower()
@field_validator("email", mode="after")
@classmethod
def validate_email_domain(cls, v: str) -> str:
if "@" not in v:
raise ValueError("Invalid email: missing '@'")
return v
上述示例中,mode='before' 的验证器先执行,去除输入中的空格并转为小写,然后 Pydantic 才检查 min_length=3 等约束。
如果需要验证依赖多个字段的逻辑(例如“开始日期必须早于结束日期”),可以使用 @model_validator:
class DateRange(BaseModel):
start: date
end: date
label: str | None = None
@model_validator(mode="after")
def check_start_before_end(self) -> DateRange:
if self.start >= self.end:
raise ValueError(
f"'start' ({self.start}) must be before 'end' ({self.end})"
)
return self
注意:After 模式的模型验证器必须 return self,否则返回 None 会导致不可空字段引发 ValidationError。
如果想在字段校验之前就重塑整个输入数据(例如让模型同时兼容元组和 dict),可以使用 mode='before' 的模型验证器:
class Coordinate(BaseModel):
x: float
y: float
@model_validator(mode="before")
@classmethod
def accept_tuple(cls, data: object) -> object:
if isinstance(data, (list, tuple)) and len(data) == 2:
return {"x": data[0], "y": data[1]}
return data
print(Coordinate.model_validate((3.0, 4.0)))
# x=3.0 y=4.0
ValidationInfo 验证上下文:借助 info.context 可以将每次调用的数据(如用户权限级别)传入验证器,而无需将这些数据加入模型本身。这在实际业务中非常实用:
class Discount(BaseModel):
price: float
discount_pct: float
@field_validator("discount_pct", mode="after")
@classmethod
def cap_discount(cls, v: float, info: ValidationInfo) -> float:
max_discount = (info.context or {}).get("max_discount", 50.0)
if v > max_discount:
raise ValueError(f"Discount cannot exceed {max_discount}%")
return v
Discount.model_validate(
{"price": 100.0, "discount_pct": 30.0},
context={"max_discount": 20.0},
)
自定义序列化器
@field_serializer 允许您精确控制某个字段在导出时的格式。例如,将 datetime 统一输出为 UTC ISO 格式:
class LogEntry(BaseModel):
message: str
timestamp: datetime
@field_serializer("timestamp")
def serialize_timestamp(self, v: datetime) -> str:
if v.tzinfo is None:
v = v.replace(tzinfo=timezone.utc)
return v.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
嵌套模型与递归结构
将一个模型直接作为另一个模型字段的类型注解,即可天然支持嵌套。Pydantic 会自动递归校验每一层:
class Employee(BaseModel):
name: str
title: str
employee_id: int = Field(gt=0)
class Department(BaseModel):
name: str
head: Employee
members: list[Employee] = []
class Company(BaseModel):
name: str
founded: int
departments: list[Department]
每个嵌套的 dict 都会按对应模型进行校验。如果 Bob 的 employee_id 传了 "not_a_number",错误信息会精确指向 departments -> 0 -> members -> 0 -> employee_id,定位非常方便。
处理自引用结构(例如树)时,只需加上 from __future__ import annotations:
from __future__ import annotations
class TreeNode(BaseModel):
value: str
children: list[TreeNode] = []
model_dump 与 model_dump_json
这两个方法是序列化的核心。它们有三种输出形态:
model_dump()输出原生 Python dict(类型保持为 Python 对象,如 datetime、Decimal)。model_dump(mode='json')输出 JSON 兼容的值(所有类型转为字符串、数字等)。model_dump_json()直接输出 JSON 字符串,绕过json.dumps(),底层走 Rust 核心,性能更佳。
三个方法均支持 exclude_unset、exclude_none、include、exclude_defaults 等过滤参数,可灵活控制序列化结果。
输入侧同理:model_validate() 解析 dict,model_validate_json() 解析原始 JSON 字符串,后者同样直接走 Rust 核心,速度更快。
别名机制分为三类:alias(输入输出都用)、validation_alias(仅输入)、serialization_alias(仅输出)。配合 AliasPath 和 AliasChoices 可处理更复杂的情况,例如嵌套访问或多个候选字段名。
JSON Schema 生成
调用 Item.model_json_schema() 即可直接输出该模型的 JSON Schema。您在 Field() 中编写的 title、description、examples 以及各种约束(如 min_length、gt)都会自动流入 schema 中,无需手动维护文档。
Pydantic Dataclasses 与 TypeAdapter
如果您喜欢使用标准库的 @dataclass 语法,但希望享受 Pydantic 的校验和约束,可以使用 from pydantic.dataclasses import dataclass。它与 BaseModel 一样支持验证器和 Field,但没有 model_dump() 等便捷方法。如果需要序列化,可通过 TypeAdapter 进行包装。
TypeAdapter 更强大的地方在于,它无需定义任何模型即可直接验证独立类型,例如:
int_list_adapter = TypeAdapter(list[int])
int_list_adapter.validate_python(["1", "2", "3"]) # [1, 2, 3]
int_list_adapter.validate_json('[4, 5, 6]') # [4, 5, 6]
它特别适合以下场景:验证函数参数、校验集合类型、为 API 类型生成 JSON Schema。
总结
最后用几个常见问题来收尾:
- field_validator 还是 model_validator? 单字段校验使用
@field_validator,精确且快速。需要同时访问多个字段时使用@model_validator(mode='after')。 - BaseModel 与 @dataclass 的区别? BaseModel 功能全面(自带序列化、schema 等)。@dataclass 语法更接近标准库,但不带模型方法,序列化需要借助 TypeAdapter。
- 如何让字段可选且带默认值? 直接写
field: str = "default"或field: str | None = None。 - 不建模型怎么验证 JSON? 使用
TypeAdapter(list[int]).validate_json('[1,2,3]')。 - 传了别名但验证器不跑?
validation_alias默认只接受别名,要同时接受原字段名可以添加ConfigDict(populate_by_name=True)。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
MyShell发布开源AI语音克隆工具OpenVoice,瞄准语音模仿领域
MyShell最新推出的开源语音克隆工具OpenVoice引起了广泛关注。这一创新产品由麻省理工学院(MIT)、清华大学以及加拿大人工智能初创公司MyShell合作开发。OpenVoice采用了一种概念简单但高效的方法,可几乎即时克隆用户的语音,并使用明显更少的计算资源。该工具不仅具备语音克隆的基本
Open Voice:轻松克隆任何声音,免费开源的AI语音克隆项目
Open Voice是由MyShell推出的一个免费开源的AI即时语音克隆项目,相较于其他的语音克隆技术,OpenVoice的优势在于仅需一段简短的音频,便能以惊人的准确度复刻说话者的音色,创造出让人信以为真的自然语音。除开复制和参考说话者的音色之外,OpenVoice还可以对语音风格进行精细控制,
VoiceCanvas-AI语音克隆 & TTS工具 | 40+种语言语音合成
VoiceCanvas是什么?VoiceCanvas是由先进AI驱动的语音克隆与文本转语音工具,支持40+种语言的即时语音合成。其核心能力包括:高质量语音合成:具有自然语调和节奏的清晰人声个性化语音克隆:通过3-10秒语音样本创建专属AI声纹多语言支持:覆盖全球主流语种的男 女声选择进阶调控功能:语
Github爆火AI语音克隆项目OpenVoice,精准进行声音复刻
最近,Github上的一个名为OpenVoice的AI语音克隆项目爆火,该项目由myshell-ai开源,仅开源了不到三周,就有了6 1k的star。OpenVoice仅需参考说话者的短音频片段,即可复制其声音并生成多种语言的语音。这一技术不仅实现了对音色的准确克隆,还在语音生成过程中提供了对情感、
Free Voice Cloning-免费AI语音克隆工具 | 5秒生成你的数字声音
Free Voice Cloning,一款真正0成本、无限制、跨语言的高质量AI语音克隆平台。适合内容创作者、教育者、AI开发者、播客人群等所有想要“复制自己的声音”并进行语音合成的用户。?️ Free Voice Cloning 是什么?Free Voice Cloning 是一个基于先进AI语音
- 热门数据榜
相关攻略
2026-07-14 17:32
2026-07-14 17:30
2026-07-14 17:30
2026-07-14 17:27
2026-07-14 17:27
2026-07-14 16:20
2026-07-14 16:19
2026-07-14 16:19
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

