python中Pydantic学习笔记
Pydantic :Python 数据验证与设置管理利器
Pydantic 是一个基于 Python 类型提示(Type Hints)的数据验证和设置管理库。它能自动将输入数据转换为指定类型,并在数据不符合要求时抛出清晰的错误信息。
2. 基础使用:定义模型
普通类定义模型有如下问题:
- ❌ 无类型检查:传入什么类型就保存什么类型
- ❌ 无数据验证:即使数据明显错误也不会提示
- ❌ 无自动转换:字符串 “123” 不会自动转成整数 123
- ❌ 容易出错:后续使用时可能因为类型错误而崩溃
# 普通类定义
class User:def __init__(self, id, name, email):self.id = idself.name = nameself.email = email# 使用普通类
user1 = User(id="123", name="Alice", email="alice@example.com")
print(user1.id) # "123" (字符串类型!)
print(type(user1.id)) # <class 'str'># 传入错误数据也不会报错
user2 = User(id="abc", name=123, email=None)
print(user2.id) # "abc" (还是字符串)
print(user2.name) # 123 (整数类型!)
print(user2.email) # None
而使用pydantic框架来定义模型则可解决问题。
2.1 最简单的模型
Pydantic 框架定义一个简单模型是通过继承 BaseModel来实现
from pydantic import BaseModelclass User(BaseModel):id: intname: stremail: str# 创建实例(自动类型转换和验证)
user = User(id="123", name="Alice", email="alice@example.com")
print(user)
# 输出: id=123 name='Alice' email='alice@example.com'# 访问属性
print(user.id) # 123 (int 类型)
print(user.name) # "Alice"
2.2 自动类型转换
创建对象后赋值会自动转换
# 字符串 "123" 自动转为 int
user = User(id="123", name=123, email="test@example.com")
# name=123 会被转为字符串 "123"
2.3 验证失败示例
当传入不兼容类型时会提示失败
try:User(id="abc", name="Bob", email="invalid-email")
except Exception as e:print(e)
# 输出: 1 validation error for User
# id
# Input should be a valid integer, unable to parse string as an integer [type=int_parsing, ...]
3. 字段类型与默认值
3.1 基本类型
from typing import Optional, List, Dict
from datetime import datetimeclass Product(BaseModel):name: strprice: floattags: List[str] = [] # 默认空列表metadata: Dict[str, str] = {} # 默认空字典created_at: datetimeis_available: bool = True # 默认 Truedescription: Optional[str] = None # 可选字段
3.2 使用 Field 进行高级配置
Field 是 Pydantic 提供的一个字段配置工具,它的主要作用是:在类型注解的基础上,为模型字段添加额外的验证规则、默认值、元数据和行为控制。
from pydantic import BaseModel, Fieldclass User(BaseModel):id: int = Field(gt=0, description="用户ID必须大于0")name: str = Field(min_length=2, max_length=50)age: int = Field(ge=0, le=120, default=18)email: str = Field(pattern=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")# 使用示例
user = User(id=1, name="Alice", email="alice@example.com")
print(user.model_dump())
# {'id': 1, 'name': 'Alice', 'age': 18, 'email': 'alice@example.com'}
常用 Field 参数:
default
: 默认值gt
,ge
,lt
,le
: 数值范围min_length
,max_length
: 字符串/列表长度pattern
: 正则表达式模式description
: 字段描述(用于文档)
4. 嵌套模型
嵌套模型 就是:一个 Pydantic 模型的字段,本身是另一个 Pydantic 模型(或模型的列表/字典)。换句话说:模型里面包含模型。
如下Address是模型User模型中的一个字段。
from typing import Listclass Address(BaseModel):street: strcity: strzipcode: strclass User(BaseModel):name: straddresses: List[Address]# 创建嵌套对象
user_data = {"name": "Bob","addresses": [{"street": "123 Main St", "city": "New York", "zipcode": "10001"},{"street": "456 Oak Ave", "city": "Boston", "zipcode": "02101"}]
}user = User(**user_data)
print(user.addresses[0].city) # "New York"
嵌套模型的强大之处如下:
- 自动递归验证
- 支持复杂嵌套结构
复杂嵌套例子
列表嵌套
class User(BaseModel):name: straddresses: List[Address] # 多个地址user = User(name="Charlie",addresses=[{"street": "123 Main St", "city": "NYC", "zipcode": "10001"},{"street": "456 Oak Ave", "city": "Boston", "zipcode": "02101"}]
)
print(user.addresses[0].city) # "NYC"
多层嵌套
class GeoLocation(BaseModel):lat: floatlng: floatclass Address(BaseModel):street: strcity: strlocation: GeoLocation # Address 里面又嵌套了 GeoLocationclass User(BaseModel):name: straddress: Address # User 嵌套 Address,Address 嵌套 GeoLocation# 三层嵌套数据
user_data = {"name": "Eve","address": {"street": "789 Pine Rd","city": "Seattle","location": {"lat": 47.6062,"lng": -122.3321}}
}user = User(**user_data)
print(user.address.location.lat) # 47.6062
5. 数据验证与自定义验证器
5.1 字段级验证器
from pydantic import BaseModel, field_validatorclass User(BaseModel):name: strpassword: str@field_validator('password')@classmethoddef password_must_contain_number(cls, v):if not any(char.isdigit() for char in v):raise ValueError('密码必须包含至少一个数字')return v# 测试
try:User(name="Alice", password="password") # 无数字
except ValueError as e:print(e) # 1 validation error for UserUser(name="Bob", password="password123") # 有效
5.2 模型级验证器
from pydantic import model_validatorclass User(BaseModel):password1: strpassword2: str@model_validator(mode='after')def passwords_match(self):if self.password1 != self.password2:raise ValueError('两次密码输入不一致')return self# 使用
User(password1="123456", password2="123456") # OK
# User(password1="123", password2="456") # 抛出异常
6. 数据序列化与反序列化
6.1 从字典创建模型
user_dict = {"id": 1, "name": "Alice", "email": "alice@example.com"}
user = User.model_validate(user_dict)
6.2 转换为字典/JSON
# 转为字典
user_dict = user.model_dump()# 转为 JSON 字符串
user_json = user.model_dump_json()# 包含默认值
user.model_dump(exclude_defaults=True) # 排除默认值
user.model_dump(include={'id', 'name'}) # 只包含指定字段
user.model_dump(exclude={'email'}) # 排除指定字段
6.3 从 JSON 字符串解析
json_str = '{"id": 1, "name": "Alice", "email": "alice@example.com"}'
user = User.model_validate_json(json_str)
7. 配置选项(ConfigDict)
from pydantic import BaseModel, ConfigDictclass User(BaseModel):model_config = ConfigDict(str_strip_whitespace=True, # 自动去除字符串首尾空格str_to_lower=True, # 字符串转小写extra='forbid', # 禁止额外字段# extra='allow' # 允许额外字段(默认)# extra='ignore' # 忽略额外字段)name: stremail: str# 测试
user = User(name=" ALICE ", email="ALICE@EXAMPLE.COM")
print(user.name) # "alice"
print(user.email) # "alice@example.com"
常用配置:
extra
: 处理额外字段的方式str_strip_whitespace
: 自动去除字符串空格str_to_lower
/str_to_upper
: 字符串大小写转换validate_assignment
: 赋值时验证(默认 False)
8. 实际应用示例
8.1 API 请求验证
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class CreateUserRequest(BaseModel):username: str = Field(min_length=3, max_length=20)email: strage: int = Field(ge=13, le=120)@app.post("/users/")
def create_user(user: CreateUserRequest):# user 已经被自动验证和转换return {"message": f"用户 {user.username} 创建成功"}
8.2 配置文件管理
from pydantic_settings import BaseSettings # 需要 pip install pydantic-settingsclass Settings(BaseSettings):database_url: strdebug: bool = Falseapi_key: strmodel_config = ConfigDict(env_file=".env")# 自动从环境变量或 .env 文件加载
settings = Settings()
print(settings.database_url)
8.3 数据清洗和标准化
class Product(BaseModel):model_config = ConfigDict(str_strip_whitespace=True)name: strprice: floatcategory: str = Field(alias="cat") # 支持字段别名# 处理不规范的输入数据
raw_data = {"name": " Laptop ", "cat": "Electronics", "price": "999.99"}
product = Product(**raw_data)
print(product.name) # "Laptop"
print(product.category) # "Electronics"
9. 高级特性
9.1 Discriminated Unions(区分联合类型)
from typing import Union
from pydantic import BaseModel, Fieldclass Cat(BaseModel):pet_type: str = Field("cat", alias="type")meows: intclass Dog(BaseModel):pet_type: str = Field("dog", alias="type")barks: intclass PetOwner(BaseModel):pet: Union[Cat, Dog] = Field(discriminator='pet_type')# 自动根据 type 字段选择正确的模型
owner1 = PetOwner(pet={"type": "cat", "meows": 5})
owner2 = PetOwner(pet={"type": "dog", "barks": 10})
9.2 自定义数据类型
from pydantic import BaseModel, GetPydanticSchema
from typing import Any
import jsonclass JsonStr(str):"""自定义 JSON 字符串类型"""@classmethoddef __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any):from pydantic_core import core_schemareturn core_schema.no_info_after_validator_function(cls._validate, core_schema.str_schema())@classmethoddef _validate(cls, value: str):try:json.loads(value)return cls(value)except json.JSONDecodeError:raise ValueError("必须是有效的 JSON 字符串")class Config(BaseModel):data: JsonStrconfig = Config(data='{"key": "value"}') # OK
# Config(data='invalid json') # 抛出异常
10. 常见错误处理
10.1 捕获验证错误
from pydantic import ValidationErrortry:User(id=-1, name="A", email="invalid")
except ValidationError as e:print("错误数量:", e.error_count())print("错误详情:", e.errors())# 输出详细的错误信息列表
10.2 自定义错误信息
from pydantic import BaseModel, field_validatorclass User(BaseModel):age: int@field_validator('age')@classmethoddef validate_age(cls, v):if v < 0:raise ValueError('年龄不能为负数')if v > 150:raise ValueError('年龄不能超过150岁')return v
总结
Pydantic 的核心优势:
✅ 类型安全:基于 Python 类型提示
✅ 自动验证:输入数据自动验证和转换
✅ 清晰错误:提供详细的验证错误信息
✅ 灵活配置:丰富的配置选项和验证器
✅ 生态集成:与 FastAPI、Django 等框架完美集成