1. 什么是类型提示? #
1.1 什么是类型提示? #
类型提示是 Python 3.6+ 的语法,用来标注变量、参数、返回值的类型。例如:
# 标注 name 是字符串类型
def greet(name: str) -> str:
return f"Hello, {name}"类型提示本身不会在运行时检查数据,它主要给 IDE 和静态检查工具用。真正的数据验证需要额外工具,Pydantic 就是其中之一。
1.2 为什么需要数据验证? #
程序经常要处理外部数据:用户输入、API 返回、配置文件等。这些数据可能格式错误、类型不对、越界等。如果直接使用,容易导致程序崩溃或产生隐蔽 bug。
Pydantic 的作用:你定义一个"数据模型"(有哪些字段、什么类型、有什么限制),Pydantic 在创建实例时自动验证,不符合就立刻报错,并给出清晰提示。
通俗比喻:就像填表时的"必填项""格式要求",Pydantic 帮你自动检查每一栏是否合格。
2. 什么是 Pydantic? #
Pydantic 是一个基于 Python 类型提示的数据验证库,特点是:
- 写法简单:用类型注解定义模型,和写普通类差不多
- 自动验证:创建实例时自动检查类型和约束
- 易序列化:轻松转成字典、JSON,或从字典、JSON 解析回来
本教程基于 Pydantic V2(当前主流版本),所有示例均可直接运行。
3. 安装 #
pip install pydantic安装完成后即可使用,无需额外配置。
4. 快速入门 #
4.1 定义第一个模型 #
用 BaseModel 定义模型,用类型注解定义字段。
# 导入 BaseModel
from pydantic import BaseModel
# 定义用户模型:继承 BaseModel
class User(BaseModel):
# 必填字段:id 为整数,name 为字符串
id: int
name: str
# 可选字段:age 默认为 18
age: int = 18
# 创建实例时自动验证类型
user = User(id=1, name="Alice")
# 打印模型(会显示所有字段)
print(user)
# 访问属性
print(user.name)
# age 使用默认值
print(user.age)4.2 验证失败会怎样? #
传入错误类型时,Pydantic 会抛出 ValidationError,并说明哪个字段有问题。
# 导入 BaseModel
from pydantic import BaseModel
# 定义模型
class User(BaseModel):
id: int
name: str
age: int = 18
# 故意传入错误类型:id 应该是 int,却传了字符串
try:
User(id="不是数字", name="Bob")
except Exception as e:
# 打印验证错误信息
print(e)5. 常用字段类型 #
5.1 基本类型 #
支持 str、int、float、bool、list、dict 等标准类型。还可以用 Optional 表示"可以为 None"。
# 导入 BaseModel 和 Optional
from pydantic import BaseModel
from typing import Optional, List
# 定义商品模型
class Product(BaseModel):
# 字符串
name: str
# 浮点数
price: float
# 字符串列表,默认空列表
tags: List[str] = []
# 可选:可以是 bool 或 None
in_stock: Optional[bool] = None
# 创建实例
p = Product(name="苹果", price=3.5, tags=["水果", "新鲜"])
print(p.name, p.price, p.tags)
# 可选字段可以不传
p2 = Product(name="香蕉", price=2.0)
print(p2.in_stock)5.2 带约束的数字类型 #
Pydantic 提供 PositiveInt(正整数)、NonNegativeInt(非负整数)等,自动拒绝不符合的值。
# 导入 BaseModel 和 PositiveInt
from pydantic import BaseModel
from pydantic import PositiveInt
# 定义模型,age 必须是正整数
class Person(BaseModel):
name: str
age: PositiveInt
# 正常创建
p = Person(name="小明", age=20)
print(p)
# 传入 0 或负数会报错(PositiveInt 要求必须大于 0)
try:
Person(name="小红", age=-1)
except Exception as e:
# 捕获 ValidationError 并打印
print("验证失败:", e)6. 使用 Field 添加约束 #
Field 可以为字段添加长度限制、取值范围、描述等。
6.1 基本用法 #
# 导入 BaseModel 和 Field
from pydantic import BaseModel, Field
# 定义商品模型
class Product(BaseModel):
# ... 表示必填;min_length、max_length 限制字符串长度
name: str = Field(..., min_length=1, max_length=50)
# gt=0 表示大于 0,le=10000 表示小于等于 10000
price: float = Field(..., gt=0, le=10000)
# 可选,最大长度 200
description: str = Field(default="", max_length=200)
# 正常创建
p = Product(name="手机", price=2999)
print(p)
# 价格超范围会报错
try:
Product(name="电脑", price=99999)
except Exception as e:
print("验证失败:", e)常用约束:min_length、max_length(字符串)、gt、ge、lt、le(数值大小)。
6.2 使用 Annotated(V2 推荐写法) #
把约束写在类型注解里,更清晰。
# 导入 Annotated、BaseModel、Field
from typing import Annotated
from pydantic import BaseModel, Field
# 用 Annotated 把类型和约束写在一起
class Product(BaseModel):
name: Annotated[str, Field(min_length=1, max_length=50)]
price: Annotated[float, Field(gt=0, le=10000)]
# 创建实例
p = Product(name="键盘", price=199)
print(p)7. 自定义验证器 #
7.1 字段验证器 #
用 @field_validator 对单个字段做自定义校验。
# 导入 BaseModel 和 field_validator
from pydantic import BaseModel, field_validator
# 定义用户模型
class User(BaseModel):
name: str
# 要求密码至少 6 位
password: str
# 验证 password 字段
@field_validator("password")
@classmethod
def password_not_short(cls, v: str) -> str:
# 长度不足则抛出 ValueError
if len(v) < 6:
raise ValueError("密码至少 6 位")
return v
# 正常创建
u = User(name="Alice", password="123456")
print(u)
# 密码太短会报错(少于 6 位)
try:
User(name="Bob", password="123")
except Exception as e:
# 捕获 ValidationError
print("验证失败:", e)7.2 跨字段验证(模型验证器) #
用 @model_validator 在多个字段都验证完后,再做整体校验。
# 导入 BaseModel 和 model_validator
from pydantic import BaseModel, model_validator
# 定义模型:两次输入的密码必须一致
class PasswordForm(BaseModel):
password1: str
password2: str
# mode='after' 表示所有字段验证完后执行
@model_validator(mode="after")
def passwords_match(self):
if self.password1 != self.password2:
raise ValueError("两次密码不一致")
return self
# 一致时正常
f = PasswordForm(password1="abc123", password2="abc123")
print("验证通过")
# 两次密码不一致时报错
try:
PasswordForm(password1="abc123", password2="abc456")
except Exception as e:
# 捕获 ValidationError
print("验证失败:", e)8. 数据解析与序列化 #
8.1 转成字典和 JSON #
# 导入 BaseModel
from pydantic import BaseModel
# 定义模型
class User(BaseModel):
id: int
name: str
age: int = 18
# 创建实例
user = User(id=1, name="Alice", age=20)
# 转成字典
user_dict = user.model_dump()
print("字典:", user_dict)
# 转成 JSON 字符串
user_json = user.model_dump_json()
print("JSON:", user_json)8.2 从字典和 JSON 解析 #
# 导入 BaseModel
from pydantic import BaseModel
# 定义模型
class User(BaseModel):
id: int
name: str
age: int = 18
# 从字典解析(会自动验证)
data = {"id": 2, "name": "Bob"}
user = User.model_validate(data)
print(user)
# 从 JSON 字符串解析
json_str = '{"id": 3, "name": "Charlie", "age": 25}'
user2 = User.model_validate_json(json_str)
print(user2)9. TypeAdapter:验证单个类型 #
当你只需要验证单个类型(如 list[int]、dict[str, int]),而不想定义完整的 BaseModel 时,可以用 TypeAdapter。它同样支持验证、解析和序列化。
9.1 基本用法 #
# 导入 TypeAdapter
from pydantic import TypeAdapter
# 创建适配器:验证 list[int] 类型
adapter = TypeAdapter(list[int])
# 从 Python 数据验证并转换
result = adapter.validate_python([1, 2, 3])
print("验证结果:", result)
# 从 JSON 字符串解析
result2 = adapter.validate_json("[10, 20, 30]")
print("解析结果:", result2)9.2 验证字典类型 #
# 导入 TypeAdapter
from pydantic import TypeAdapter
# 验证 dict[str, int]:键为字符串,值为整数
adapter = TypeAdapter(dict[str, int])
# 从字典验证
data = adapter.validate_python({"a": 1, "b": 2, "c": 3})
print("字典:", data)
# 从 JSON 解析
json_str = '{"x": 10, "y": 20}'
data2 = adapter.validate_json(json_str)
print("解析:", data2)9.3 序列化 #
# 导入 TypeAdapter
from pydantic import TypeAdapter
# 验证 list[str]
adapter = TypeAdapter(list[str])
data = ["a", "b", "c"]
# 转成字典(对 list 来说就是 list 本身)
dumped = adapter.dump_python(data)
print("dump_python:", dumped)
# 转成 JSON 字符串(dump_json 返回 bytes,需 decode 成 str)
json_str = adapter.dump_json(data).decode()
print("dump_json:", json_str)说明:TypeAdapter 适合"只关心一个类型"的场景,如 API 返回的 list、配置中的 dict。需要多个字段的结构化数据时,仍推荐用 BaseModel。
10. 常用配置 #
通过 model_config 可以调整模型行为。
# 导入 BaseModel 和 ConfigDict
from pydantic import BaseModel, ConfigDict
# 定义模型
class User(BaseModel):
# 禁止创建后修改字段(类似 frozen)
model_config = ConfigDict(frozen=True)
id: int
name: str
# 创建实例
user = User(id=1, name="Alice")
print(user)
# 尝试修改会报错
try:
user.name = "Bob"
except Exception as e:
print("修改失败:", e)常用配置:frozen=True(不可变)、extra='forbid'(禁止额外字段)。
11. 模型继承 #
子类会继承父类的字段。
# 导入 BaseModel
from pydantic import BaseModel
# 父类:人
class Person(BaseModel):
name: str
age: int
# 子类:员工,继承 Person 并新增字段
class Employee(Person):
employee_id: str
# 创建员工实例,需要填父类+子类的字段
emp = Employee(name="张三", age=30, employee_id="E001")
print(emp)12. 常见异常 #
| 异常 | 何时抛出 |
|---|---|
ValidationError |
数据不符合模型定义或约束 |
| 访问不存在的字段 | 模型未定义该字段 |
用 try/except 捕获 ValidationError 即可处理验证失败。
13. 总结 #
| 内容 | 要点 |
|---|---|
| 定义模型 | 继承 BaseModel,用类型注解定义字段 |
| 字段约束 | 用 Field 或 Annotated 添加长度、范围等 |
| 自定义验证 | @field_validator 单字段,@model_validator 跨字段 |
| 序列化 | model_dump() 转字典,model_dump_json() 转 JSON |
| 解析 | model_validate() 从字典,model_validate_json() 从 JSON |
| TypeAdapter | 验证单个类型(如 list[int]、dict[str, int]),无需定义模型 |
记忆口诀:先定义模型,再创建实例;数据不对会立刻报错,转字典/JSON 用 model_dump 和 model_validate;单类型验证用 TypeAdapter。