名称: pydantic 描述: 使用Python类型注解进行数据验证和设置管理,基于Pydantic v2 使用场景: 当您需要验证数据结构、创建设置模型、序列化/反序列化数据,或在Python应用程序中确保类型安全时
Pydantic v2 框架技能
Pydantic是一个数据验证库,它使用Python类型注解来定义数据模式,提供快速且可扩展的验证,并支持自动类型转换。
快速开始
基础模型定义
from pydantic import BaseModel
from datetime import datetime
from typing import Optional
class User(BaseModel):
id: int
name: str
email: str
signup_ts: Optional[datetime] = None
is_active: bool = True
# 自动类型转换
user = User(
id='123', # 字符串 → 整数
name='John Doe',
email='john@example.com',
signup_ts='2017-06-01 12:22' # 字符串 → 日期时间
)
从数据源验证
# 从字典
user = User.model_validate({'id': 1, 'name': 'Alice', 'email': 'alice@test.com'})
# 从JSON
user = User.model_validate_json('{"id": 1, "name": "Alice", "email": "alice@test.com"}')
# 序列化
print(user.model_dump()) # Python字典
print(user.model_dump_json()) # JSON字符串
常用模式
字段配置
from pydantic import BaseModel, Field, EmailStr, HttpUrl
from typing import Annotated
class Product(BaseModel):
product_id: int = Field(alias='id', ge=1, description='唯一产品标识符')
name: str = Field(min_length=1, max_length=200)
price: float = Field(gt=0, le=1000000)
email: EmailStr
website: HttpUrl
tags: list[str] = Field(default_factory=list, max_length=10)
internal_code: str = Field(exclude=True, default='N/A')
class User(BaseModel):
username: Annotated[str, Field(min_length=3, pattern=r'^[a-zA-Z0-9_]+$')]
age: int = Field(ge=0, le=150)
模型配置
from pydantic import BaseModel, ConfigDict
class StrictModel(BaseModel):
model_config = ConfigDict(
strict=True, # 无类型转换
frozen=True, # 不可变实例
validate_assignment=True, # 属性赋值时验证
extra='forbid', # 拒绝额外字段
str_strip_whitespace=True,
populate_by_name=True, # 接受别名和字段名
use_enum_values=True, # 序列化枚举为值
)
id: int
name: str
自定义验证
from pydantic import BaseModel, model_validator, field_validator, ValidationError
from typing import Any
class DateRange(BaseModel):
start_date: str
end_date: str
@field_validator('start_date', 'end_date')
@classmethod
def validate_date_format(cls, v: str) -> str:
# 自定义验证逻辑
if not v:
raise ValueError('日期不能为空')
return v
@model_validator(mode='after')
def check_dates_order(self) -> 'DateRange':
# 跨字段验证
if self.start_date > self.end_date:
raise ValueError('开始日期必须在结束日期之前')
return self
# 使用模型
try:
date_range = DateRange(start_date='2024-01-01', end_date='2024-01-31')
except ValidationError as e:
for error in e.errors():
print(f"{error['loc']}: {error['msg']}")
序列化控制
from pydantic import BaseModel, Field, SecretStr
from datetime import datetime
class User(BaseModel):
id: int
username: str
password: SecretStr
created_at: datetime
internal_data: dict = Field(exclude=True, default_factory=dict)
# 序列化选项
user = User(
id=1,
username='john',
password='secret',
created_at=datetime.now()
)
# 基础序列化
print(user.model_dump()) # Python字典
print(user.model_dump_json()) # JSON字符串
# 排除字段
print(user.model_dump(exclude={'password'}))
print(user.model_dump(exclude={'username', 'created_at'}))
# 仅包含特定字段
print(user.model_dump(include={'id', 'username'}))
# JSON兼容序列化
print(user.model_dump(mode='json')) # 日期时间 → 字符串
print(user.model_dump(by_alias=True)) # 使用字段别名
自定义序列化
from typing import Annotated, Any
from pydantic import BaseModel, field_serializer, PlainSerializer
class Model(BaseModel):
number: int
created_at: datetime
@field_serializer('number')
def serialize_number(self, value: int) -> str:
return f"{value:,}" # 用逗号格式化
# 使用Annotated和PlainSerializer
custom_field: Annotated[
float,
PlainSerializer(lambda x: round(x, 2), return_type=float)
]
嵌套模型和关系
from pydantic import BaseModel
from typing import Optional, List
class Address(BaseModel):
street: str
city: str
country: str = 'USA'
zip_code: str
class User(BaseModel):
id: int
name: str
addresses: List[Address]
primary_address: Optional[Address] = None
# 使用
user = User(
id=1,
name='John Doe',
addresses=[
{'street': '123 Main St', 'city': 'New York', 'zip_code': '10001'},
{'street': '456 Oak Ave', 'city': 'Boston', 'zip_code': '02101'}
],
primary_address={'street': '123 Main St', 'city': 'New York', 'zip_code': '10001'}
)
枚举集成
from enum import Enum, IntEnum
from pydantic import BaseModel
class Status(str, Enum):
PENDING = 'pending'
ACTIVE = 'active'
COMPLETED = 'completed'
class Priority(IntEnum):
LOW = 1
MEDIUM = 2
HIGH = 3
class Task(BaseModel):
title: str
status: Status = Status.PENDING
priority: Priority = Priority.MEDIUM
model_config = ConfigDict(use_enum_values=True)
# 可以使用枚举值或名称
task1 = Task(title='任务1', status='active', priority=3)
task2 = Task(title='任务2', status=Status.ACTIVE, priority=Priority.HIGH)
TypeAdapter用于独立验证
from pydantic import TypeAdapter
from typing import List, Optional
# 无需完整模型验证单个类型
int_adapter = TypeAdapter(int)
print(int_adapter.validate_python('123')) # 123
list_adapter = TypeAdapter(List[int])
print(list_adapter.validate_python(['1', '2', '3'])) # [1, 2, 3]
# 生成JSON模式
print(int_adapter.json_schema())
print(list_adapter.json_schema())
数据验证模式
from pydantic import BaseModel, ValidationError
from typing import Union
class EmailValidator(BaseModel):
email: str
@field_validator('email')
@classmethod
def validate_email(cls, v: str) -> str:
if '@' not in v:
raise ValueError('无效的邮箱格式')
return v.lower()
# 验证错误处理
try:
user = User(id='invalid', name='', email='test')
except ValidationError as e:
print(f"错误: {e.error_count()}")
for error in e.errors():
print(f" {error['loc']}: {error['msg']} ({error['type']})")
要求
- Python 3.8+
- Pydantic v2.x:
uv add pydantic - 增强类型的可选依赖:
uv add pydantic[email]用于EmailStruv add pydantic[url]用于HttpUrluv add pydantic[typing-extensions]用于扩展类型支持
最佳实践
- 使用特定类型: 对于正数,优先使用
conint(gt=0)而非int - 配置模型: 使用
ConfigDict设置全局模型行为 - 处理验证错误: 始终在try/catch块中包装模型创建
- 使用字段验证器: 使用
@field_validator实现自定义验证逻辑 - 控制序列化: 使用
model_dump()参数控制输出格式 - 利用类型转换: Pydantic自动转换兼容类型
- 使用嵌套模型: 将复杂数据分解为更小、可重用的模型