当前位置:首页>python>Pydantic v2:Python 数据验证的正确打开方式

Pydantic v2:Python 数据验证的正确打开方式

  • 2026-10-11 08:13:22
Pydantic v2:Python 数据验证的正确打开方式


一、数据验证的痛点

写接口时,你一定遇到过这种代码:

defcreate_user(name, age, email):
ifnot name:
raise ValueError("name 不能为空")
ifnotisinstance(age, int):
raise TypeError("age 必须是整数")
if age < 0or age > 150:
raise ValueError("age 不合理")
if"@"notin email:
raise ValueError("email 格式错误")
# ... 业务逻辑

这种手动验证的代码,写起来繁琐、阅读性差、维护成本高。而且散落在项目各处,格式不统一。

Pydantic 解决的就是这个问题——用声明式的数据模型,自动完成解析、验证、序列化。


二、核心用法:BaseModel

Pydantic 的核心是 BaseModel,用它定义数据结构:

from pydantic import BaseModel, EmailStr, Field

classUser(BaseModel):
    name: str
    age: int = Field(ge=0, le=150)
    email: EmailStr
    hobby: list[str] = []

自动验证:

# 正常数据
user = User(name="张三", age=28, email="zhangsan@example.com", hobby=["读书", "游泳"])
print(user.model_dump())
# {'name': '张三', 'age': 28, 'email': 'zhangsan@example.com', 'hobby': ['读书', '游泳']}

# 验证失败:类型错误
try:
    User(name="李四", age="三十", email="invalid-email")
except Exception as e:
print(e)
# 1 validation error for User
# age
#   Input should be a valid integer [type=int_type, input_value='三十', input_type=str]

类型不匹配、格式错误、范围越界,Pydantic 全程自动拦截。


三、字段约束:Field 详解

Field 是 Pydantic 最重要的工具,覆盖了几乎所有常用约束:

from pydantic import BaseModel, Field, field_validator

classProduct(BaseModel):
# 字符串约束
    name: str = Field(min_length=2, max_length=50)
    desc: str | None = Field(default=None, max_length=500)

# 数值约束
    price: float = Field(gt=0, description="价格必须大于0")
    stock: int = Field(ge=0, le=999999)

# 正则约束
    phone: str = Field(pattern=r"^1[3-9]\d{9}$")

# 定制验证器
    @field_validator("price")
    @classmethod
defprice_must_be_positive(cls, v):
if v <= 0:
raise ValueError("价格必须大于0")
returnround(v, 2)  # 自动保留2位小数

所有约束在解析阶段就完成,不用写一行 if-else。


四、嵌套模型:复杂数据结构

from pydantic import BaseModel, EmailStr

classAddress(BaseModel):
    province: str
    city: str
    district: str
    detail: str

classOrder(BaseModel):
    order_id: str
    user_email: EmailStr
    shipping_address: Address
    items: list[dict]  # 任意结构
    note: str | None = None

# Pydantic 自动递归解析嵌套数据
order = Order(
    order_id="ORD-2024-001",
    user_email="customer@example.com",
    shipping_address={
"province": "广东",
"city": "深圳",
"district": "南山区",
"detail": "科技园A栋1001"
    },
    items=[{"product_id": "P001", "qty": 2}, {"product_id": "P003", "qty": 1}]
)

print(order.shipping_address.city)  # 深圳
print(order.items[0]["product_id"])  # P001

字典进去,对象出来——JSON 数据直接变成强类型的 Python 对象。


五、API 接口集成:FastAPI 示例

Pydantic 是 FastAPI 的基石,两者配合使用:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

classUserCreate(BaseModel):
    name: str = Field(min_length=2, max_length=30)
    email: EmailStr
    age: int = Field(ge=18, le=100)

classUserResponse(BaseModel):
id: int
    name: str
    email: EmailStr
    age: int

@app.post("/users", response_model=UserResponse)
defcreate_user(user: UserCreate):
# user 参数直接就是验证通过后的 UserCreate 对象
# 不用手动验证,直接写业务逻辑
    new_user = db.create(user.model_dump())
return UserResponse(id=new_user.id, **user.model_dump())

FastAPI 自动用 Pydantic 做请求解析和响应序列化,接口定义干净利落。


六、数据序列化:模型与字典/JSON 互转

from pydantic import BaseModel
from datetime import datetime

classEvent(BaseModel):
    title: str
    start_time: datetime
    tags: list[str] = []

event = Event(title="技术分享", start_time="2024-12-01T14:00:00", tags=["Python", "后端"])

# Pydantic 模型 → 字典
print(event.model_dump())
# {'title': '技术分享', 'start_time': datetime.datetime(2024, 12, 1, 14, 0), 'tags': ['Python', '后端']}

# Pydantic 模型 → JSON 字符串
print(event.model_dump_json())
# {"title":"技术分享","start_time":"2024-12-01T14:00:00","tags":["Python","后端"]}

# 字典 → Pydantic 模型
data = {"title": "新活动", "start_time": "2024-12-15T10:00:00", "tags": []}
new_event = Event.model_validate(data)

七、必填与可选:默认值处理

from pydantic import BaseModel, Field

classConfig(BaseModel):
# 必须有值,不传直接报错
    app_name: str

# 有默认值,可选
    debug: bool = False
    version: str = "1.0.0"

# 显式标记可选(等价于 str | None)
    description: str | None = None

# 列表默认空列表(注意写法)
    allowed_origins: list[str] = Field(default_factory=list)

# 不传可选字段,使用默认值
config = Config(app_name="MyApp")
print(config.debug)  # False
print(config.allowed_origins)  # []

八、敏感数据处理:Field 高级用法

from pydantic import BaseModel, Field, computed_field

classUser(BaseModel):
    username: str
    password: str = Field(exclude=True)  # 序列化时排除,不进入 dict/json
    email: str

    @computed_field
    @property
defmasked_email(self) -> str:
# 邮箱脱敏:zhangsan@example.com → z****@example.com
        name, domain = self.email.split("@")
returnf"{name[0]}{'*' * (len(name)-1)}@{domain}"

user = User(username="zhangsan", password="Secret123", email="zhangsan@example.com")

print(user.model_dump())
# {'username': 'zhangsan', 'email': 'zhangsan@example.com'}  # password 被排除

print(user.masked_email)
# z****@example.com

exclude=True 防止密码泄露到日志和 API 响应里。


九、泛型模型:复用验证逻辑

from pydantic import BaseModel, Field
from typing importGeneric, TypeVar

T = TypeVar("T")

classApiResponse(BaseModel, Generic[T]):
    code: int = Field(ge=0, le=9999)
    message: str
    data: T | None = None

classPageInfo(BaseModel):
    page: int = Field(ge=1)
    page_size: int = Field(ge=1, le=100)

# 定义具体响应类型
classUserListResponse(ApiResponse[list[dict]]):
pass

classUserPageResponse(ApiResponse[PageInfo]):
pass

# data 类型自动验证
success_response = UserListResponse(
    code=0,
    message="success",
    data=[{"id": 1, "name": "张三"}, {"id": 2, "name": "李四"}]
)

print(success_response.model_dump_json(indent=2))

泛型模型让接口响应格式统一,同时保留类型安全。


十、常见坑与避坑指南

坑一:默认值用 [] 导致共享问题

classBug(BaseModel):
    tags: list[str] = []  # ❌ 危险!所有实例共享同一个列表

# 正确做法
from pydantic import Field
classCorrect(BaseModel):
    tags: list[str] = Field(default_factory=list)  # ✅ 每次实例化独立列表

坑二:嵌套模型中可选字段需要显式 | None

classA(BaseModel):
    b: "B" | None = None# 必须显式写 None,默认值才能正确处理

classB(BaseModel):
    value: str

坑三:验证器优先级

from pydantic import field_validator, model_validator

classExample(BaseModel):
    x: int
    y: int

    @field_validator("x", "y")
    @classmethod
defcheck_positive(cls, v):
if v < 0:
raise ValueError("must be positive")
return v

    @model_validator(mode="after")
defcheck_sum(self):
ifself.x + self.y > 100:
raise ValueError("x + y 不应超过100")
returnself

十一、性能对比

Pydantic v2 相比 v1 有巨大性能提升:

操作
Pydantic v1
Pydantic v2
提升
基础模型解析
100ms
18ms
5.5x
嵌套模型验证
250ms
45ms
5.5x
JSON 序列化
80ms
12ms
6.7x

v2 底层重写,用 Rust 实现核心路径,性能已接近原生 C 库。


十二、总结

Pydantic 解决的是 Python 长期存在的数据验证混乱问题:

  • • 告别手动 if-else 验证:声明式模型,约束自动生效
  • • 统一数据格式:API 请求、配置文件、数据库记录全部用同一套模型
  • • 类型安全:IDE 自动补全,静态分析工具全程护航
  • • v2 性能飞跃:不再是"优雅但慢"的代表

FastAPI、Dalton、Gradio 等主流框架都在用 Pydantic 作为数据层基础。

GitHub: https://github.com/pydantic/pydantic

官方文档: https://docs.pydantic.dev

最新文章

随机文章