Pydantic v2 核心:类型注解与模型
本教程共 50 篇 · 第 19 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:学会用 Python 类型注解定义 Pydantic 数据模型,能给字段加约束、标可选,并能创建对象、读属性。
FastAPI 处理请求数据、返回响应,背后都靠一个库撑着,它叫 Pydantic。你可以把它理解成「数据守门员」:你先说清楚数据长什么样,它帮你检查、转换、报错。本章只讲 Pydantic v2 的核心用法,把地基打牢,后面的校验器、序列化才好懂。
19-1 类型提示为什么重要
Python 3.5 引入了类型提示(Type Hints,用于函数参数与返回值注解),3.6 又加入了变量注解语法。它不改变程序运行结果,只是给变量、参数、返回值贴一个「类型标签」。
def get_full_name(first_name: str, last_name: str) -> str:
return first_name.title() + " " + last_name.title()
print(get_full_name("john", "doe"))
这里 first_name: str 就是类型提示。编辑器看到它,就能在你写代码时提示方法、标出错误。FastAPI 更是把类型提示玩到了极致:你声明什么类型,它就帮你校验、转换、生成文档。
Note类型提示只是「提示」。下面的代码不会在运行时自动报错,真正帮你拦住脏数据的是 Pydantic。
19-2 标准类型注解
Pydantic 模型里的字段,用的就是这些基础类型:int、float、str、bool、bytes。写法直接在字段后面加冒号。
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
on_sale: bool
stock: int
这四个字段都是「必需」的,创建对象时必须都给值,否则 Pydantic 直接抛错。所谓必需,就是字段没有默认值。
19-3 定义你的第一个 BaseModel
Pydantic 里所有数据模型都继承自 BaseModel。你把字段写成类属性,每个属性带一个类型提示即可。
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
age: int
u = User(id=1, name="小明", age=18)
print(u.name) # 小明
print(u) # id=1 name='小明' age=18
User(id=1, name="小明", age=18) 就是在实例化模型。创建好后,u.name 直接拿到属性值,编辑器也会提示这些属性。
19-4 默认值让字段变成可选
如果一个字段给了默认值,那它就不是必需的了。常见做法是用 None 当默认值,表示「可以不传」。
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
description: str | None = None
tax: float | None = None
这里 description 和 tax 都有默认值 None,所以下面两种数据都合法:
Item(name="手机", price=2999.0)
Item(name="手机", price=2999.0, description="新款", tax=0.1)
Tip现代 Python(3.10+)推荐用
str | None这种写法,比老的Optional[str]更直观。本教程全程使用X | None。
19-5 用 Field() 给字段加约束
光有类型还不够。比如价格不能是负数,名字不能太长,这时就用 Field()。它从 pydantic 直接导入,用来给字段加校验和说明。
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str = Field(..., max_length=300, description="商品名称")
price: float = Field(..., gt=0, description="价格必须大于 0")
stock: int = Field(default=0, ge=0, description="库存数量")
几个常用参数要记住:
...表示「必填,没有默认值」。default=0给一个默认填充值。gt(大于)、ge(大于等于)、lt(小于)、le(小于等于)做数值范围。max_length、min_length限制字符串长度。description、title写给人看的说明,会进文档。
如果传进来的价格是负数,gt=0 会立刻拦下并报告错误。
19-6 标准泛型:list、dict、tuple、set
字段里常要放「一组值」。Python 3.9 之后可以直接用 list[str]、dict[str, int] 这种标准写法,不用再从 typing 导入大写开头的 List、Dict。
from pydantic import BaseModel
class Student(BaseModel):
id: int
name: str
subjects: list[str] = []
scores: dict[str, int] = {}
tags: set[str] = set()
这表示:subjects 是个字符串列表,scores 是「科目名 → 分数」的字典,tags 是字符串集合。Pydantic 会检查里面每个元素的类型对不对。
s = Student(
id=1,
name="小红",
subjects=["语文", "数学"],
scores={"语文": 90, "数学": 95},
)
print(s.subjects) # ['语文', '数学']
19-7 嵌套模型
模型里还能嵌套另一个模型。比如订单里包含多个商品,每个商品又是一个独立模型。
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str
price: float = Field(..., gt=0)
class Order(BaseModel):
order_id: int
items: list[Product]
items 是一个 Product 列表。你传进去的字典列表,Pydantic 会自动逐个变成 Product 对象。
order = Order(
order_id=101,
items=[
{"name": "键盘", "price": 199.0},
{"name": "鼠标", "price": 99.0},
],
)
print(order.items[0].name) # 键盘
19-8 自动类型转换很贴心
Pydantic 会尽量把能转的类型转过去。比如你传字符串 "18" 给 int 字段,它能自动变成整数 18。
from pydantic import BaseModel
class User(BaseModel):
id: int
age: int
u = User(id="1", age="18")
print(u.id, type(u.id)) # 1 <class 'int'>
但如果完全转不了,比如把列表传给 int,Pydantic 就抛出 ValidationError,清楚告诉你哪个字段、什么错。
Tip这种「宽松转换」是 Pydantic 的默认行为。想更严格可以配置
strict=True,但初学阶段先用默认即可。
19-9 在 FastAPI 里直接用模型收请求体
模型最常用在 API 的请求体。你只要把参数类型声明成模型,FastAPI 就自动读 JSON、校验、转换。
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Any
class Item(BaseModel):
name: str
price: float = Field(..., gt=0)
description: str | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item) -> dict[str, Any]:
return item.model_dump()
客户端发来的 JSON 会被塞进 Item,校验通过后你就能用 item.name、item.price 直接取用。一旦价格不合法,FastAPI 自动返回 422 错误和详细原因,你不用手写任何判断。
19-10 数据不合法时长什么样
学会「正确的写法」还不够,你还得知道「错的时候报什么」,调试时才不慌。当某字段违反约束,Pydantic 抛出 ValidationError,它会逐条列出错误。
from pydantic import BaseModel, Field, ValidationError
class Item(BaseModel):
name: str = Field(..., max_length=5)
price: float = Field(..., gt=0)
try:
Item(name="这是一个超长的名字", price=-1)
except ValidationError as e:
print(e)
错误信息会告诉你:哪个字段(name)、什么类型错误(string_too_long)、输入值是什么。在 FastAPI 里,这种错误会被自动变成 422 响应,你不用自己写判断。
Tip想看完整结构,可以把错误转成 JSON:
e.errors()返回列表,每项含loc、type、msg等键,方便你写前端提示。
19-11 为什么不直接用字典
有人会问:我用普通 dict 存数据不行吗?行,但你会失去三样东西。第一,没有编辑器提示,写 item["nmae"] 拼错也不会被发现。第二,没有自动类型转换,"18" 永远是字符串。第三,没有统一校验,每个函数都要自己写一堆 if。Pydantic 把这三件事一并包办,还顺手生成接口文档。
19-12 小结
本章你掌握了 Pydantic v2 的地基:BaseModel 定义模型、用类型注解声明字段、用 Field() 加约束、用 X | None 标可选、用 list[str] 等标准泛型描述集合,以及创建对象、读属性、自动转换、查看校验错误。记住,所有写法都是 v2 风格——没有 Optional、没有旧式 List、没有 .dict()。下一章我们给模型加上「自定义校验逻辑」。