1)Pydantic 是什么?
Pydantic 是 Python 生态中最常用的数据验证与序列化库:你用类型注解描述数据结构,Pydantic 负责在运行时做验证、类型转换、错误报告与 JSON Schema 生成。
2)为什么使用 Pydantic?
- 类型驱动:直接复用 Python 类型提示,和 IDE、mypy/pyright 协同良好。
- 高性能:核心验证引擎
pydantic-core用 Rust 实现,速度在同类库中很有竞争力。 - 序列化能力强:统一支持导出 Python 对象、JSON 兼容对象与 JSON 字符串。
- JSON Schema 生成:模型可直接生成 JSON Schema(2020-12),天然适配 OpenAPI 3.1。
- 严格/宽松双模式:可做智能转换(默认)也可强校验(
strict=True)。 - 可扩展:验证器、序列化器、TypeAdapter、自定义类型协议都很完善。
- 生态成熟:FastAPI、SQLModel、Django Ninja、LangChain 等大量项目依赖。
3)Pydantic 解决了哪些工程痛点?
- 外部输入不可信:自动拦截脏数据并定位具体字段错误。
- 类型漂移:把字符串数字、日期字符串等按规则转换为目标类型。
- 接口契约不稳定:通过模型统一入参/出参,降低前后端沟通成本。
- 文档同步困难:模型即文档,Schema 自动生成。
5)Pydantic v2 必须掌握的 API
model_validate(obj):输入 Python 对象(如dict、ORM 对象),执行验证和类型转换后,返回模型实例。适合服务内已经拿到 Python 数据的场景。model_validate_json(json_data):输入 JSON 字符串/字节串,一步完成“JSON 解析 + 校验 + 转换”。常用于高性能入口(HTTP 原始报文、消息队列消息体)。model_dump(...):导出为 Python 字典。常用参数:exclude_unset(只导出已设置字段)、exclude_defaults(排除默认值字段)、exclude_none(排除None字段)、mode="json"(导出 JSON 兼容值)。model_dump_json(...):直接导出为 JSON 字符串,适合写日志、发消息、直接返回文本化载荷。model_json_schema():生成模型对应的 JSON Schema,是 OpenAPI 文档、前端自动表单和契约校验工具链的基础。
6)序列化三种常见形态
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")
# 形态 1:Python 对象字典(保留 Python 原生类型)
# exclude_unset=True:只导出“显式传入/被设置过”的字段
# 例如 datetime 仍是 datetime 对象、bytes 仍是 bytes
print(m.model_dump(exclude_unset=True))
# 形态 2:JSON 兼容字典(仍是 dict,但值已可 JSON 化)
# mode="json":把 datetime/bytes/UUID 等转换为 JSON 友好值
# exclude={"where"}:排除指定字段
print(m.model_dump(mode="json", exclude={"where"}))
# 形态 3:JSON 字符串(可直接落日志/消息队列/HTTP)
# exclude_defaults=True:排除“等于默认值”的字段,减少冗余
print(m.model_dump_json(exclude_defaults=True))
7)严格模式 vs 宽松模式
- 宽松模式(默认):更偏业务友好,允许合理类型转换。
- 严格模式:更偏安全与一致性,不做隐式转换。
实践建议:外部入口(支付、风控、权限)可偏严格;内部服务交互可按业务选择宽松/严格策略。
8)除了 BaseModel,还有哪些建模方式?
BaseModel:最常用,功能最完整。pydantic.dataclasses.dataclass:给 dataclass 增加验证能力。TypeAdapter:对任意类型(如TypedDict、list[int])做验证/序列化。validate_call:为函数调用参数做运行时验证。
9)TypeAdapter 的典型场景
from typing_extensions import TypedDict
from pydantic import TypeAdapter
class Query(TypedDict):
page: int
size: int
adapter = TypeAdapter(Query)
data = adapter.validate_python({"page": "1", "size": "20"})
print(data)
print(adapter.json_schema())
当你不想额外定义 BaseModel,但仍要验证类型时,TypeAdapter 很高效。
10)自定义验证与序列化
先记住一句话:验证器(Validator)用于“输入阶段”,序列化器(Serializer)用于“输出阶段”。
field_validator:字段级规则,适合单字段校验/修正(如用户名格式)。model_validator:模型级规则,适合跨字段一致性(如密码与确认密码一致)。field_serializer:输出阶段格式化/脱敏(如时间格式化、手机号打码)。
from datetime import datetime, timezone
from pydantic import BaseModel, field_serializer, field_validator, model_validator
class UserProfile(BaseModel):
username: str
password: str
confirm_password: str
created_at: datetime
phone: str
# 1) 字段级验证器:校验并修正单字段输入
@field_validator("username")
@classmethod
def normalize_username(cls, value: str) -> str:
if " " in value:
raise ValueError("用户名不能包含空格")
return value.strip().title()
# “now”转时间 + 时区修正到 UTC
@field_validator("created_at", mode="before")
@classmethod
def parse_created_at(cls, value: object) -> datetime:
if isinstance(value, str) and value.lower() == "now":
return datetime.now(timezone.utc)
if isinstance(value, str):
# Python 3.13 推荐:fromisoformat 解析 ISO8601 字符串
value = datetime.fromisoformat(value)
if isinstance(value, datetime):
# 统一输出为 UTC 感知时间
if value.tzinfo is None:
return value.replace(tzinfo=timezone.utc)
return value.astimezone(timezone.utc)
raise TypeError("created_at 必须是 datetime 或 ISO8601 字符串")
# 2) 模型级验证器:处理跨字段业务规则
@model_validator(mode="after")
def check_passwords_match(self) -> "UserProfile":
if self.password != self.confirm_password:
raise ValueError("两次密码输入不一致")
return self
# 3) 字段级序列化器:控制输出给前端的格式
@field_serializer("created_at")
def serialize_created_at(self, dt: datetime) -> str:
return dt.strftime("%Y-%m-%d %H:%M:%S %Z")
@field_serializer("phone")
def serialize_phone(self, value: str) -> str:
if len(value) >= 7:
return f"{value[:3]}****{value[-4:]}"
return "****"
payload = {
"username": "alice",
"password": "secret123",
"confirm_password": "secret123",
"created_at": "now",
"phone": "13812345678",
}
user = UserProfile.model_validate(payload)
print(user.model_dump())
print(user.model_dump_json())
面试回答可总结:field_validator 管单字段输入质量,model_validator 管跨字段业务一致性,field_serializer 管输出格式与脱敏。
11)Pydantic 与 FastAPI 的关系
- 请求体校验:非法输入自动 422。
- 响应模型过滤:避免敏感字段泄漏。
- OpenAPI 生成:模型直接驱动文档。
面试可总结:FastAPI 的“自动化体验”很大程度来自 Pydantic。