PEP 593 新增的Annotated 类型
PEP 593 引入的 Annotated(在 typing 模块中)主要作用是:
一句话理解
给类型附加额外的元数据(metadata),同时不改变原有类型语义。
它的形式是:
from typing import Annotated
x: Annotated[int, metadata]
这里:
int是真实类型(静态类型检查器仍认为它是int)metadata是附加信息,供框架、库、运行时工具使用。
为什么需要它?
以前:
类型提示只负责:
age: int
只能表达“这是 int”。
但现实中经常需要表达:
- 这个 int 必须大于 0
- 这个字符串是邮箱
- 这个参数来自 HTTP Header
- 这个字段数据库里是主键
单靠类型系统无法表达。
Annotated 就是把这些信息塞进去:
from typing import Annotated
age: Annotated[int, "must be positive"]
主要作用
1. 给类型加约束(Validation)
常见于 Pydantic
from typing import Annotated
from pydantic import BaseModel, Field
class User(BaseModel):
age: Annotated[int, Field(gt=0, lt=150)]
这里:
int
还是类型;
Field(gt=0)
是元数据。
作用:
- 类型检查仍认为是 int
- Pydantic 用 metadata 做运行时验证
2. 给框架提供额外语义
比如 FastAPI:
from typing import Annotated
from fastapi import Header
async def endpoint(
token: Annotated[str, Header()]
):
...
str 是类型。
Header() 表示:
这个参数来自 HTTP Header。
非常优雅。
3. 自定义元数据(领域标记)
from typing import Annotated
UserId = Annotated[int, "primary key"]
id: UserId = 42
你可以附加业务语义。
甚至:
class Range:
def __init__(self, min_, max_):
self.min = min_
self.max = max_
Score = Annotated[int, Range(0, 100)]
4. 让静态类型系统和运行时工具解耦
这是 PEP 593 的核心设计思想。
类型检查器:
Annotated[int, xxx]
默认等价于:
int
元数据不会破坏类型系统。
而运行时工具可读取:
from typing import get_type_hints
print(get_type_hints(obj, include_extras=True))
可以拿到:
Annotated[int, ...]
和普通 type hint 区别
普通:
name: str
只有类型。
Annotated:
name: Annotated[str, MaxLen(20)]
类型 + 元数据。
本质像:
类型本身 + 注释插件
PEP 593 出现前怎么做?
以前常这样:
from pydantic import Field
age: int = Field(gt=0)
约束和默认值混在一起。
PEP 593 后更干净:
age: Annotated[int, Field(gt=0)]
类型和元数据分离。
多个 metadata
可以叠:
from typing import Annotated
x: Annotated[
int,
"positive",
"database index",
]
多个框架可以各取所需。
实际最经典例子
FastAPI 参数声明
from typing import Annotated
from fastapi import Query
async def search(
q: Annotated[str, Query(min_length=3)]
):
...
相当于:
- 类型是
str - 参数来自 query string
- 最小长度 3
全放在一个声明里。
类型检查怎么看?
from typing import Annotated
x: Annotated[int, "foo"]
y: int = x # 没问题
像 mypy 通常把它当 int。
它不是做什么的?
不是新类型:
Annotated[int, ...]
不是 PositiveInt 这种新类型。
只是给 int 加说明。
类比理解
像数据库:
age INT
只是类型。
而:
age INT CHECK(age > 0)
类型 + 元信息/约束。
Annotated 很像这个。
PEP 593 设计目标(官方总结)
主要是为:
- 类型之外携带 metadata
- 支持框架扩展
- 不影响静态类型检查
什么时候该用?
适合:
- FastAPI 参数声明
- Pydantic v2
- ORM 字段描述
- 自定义验证系统
- 类型驱动元编程
不适合:
只是普通变量注释时没必要:
x: int
够了。
一个现代 Python 风格示例(推荐写法)
from typing import Annotated
from pydantic import BaseModel, Field
PositiveInt = Annotated[int, Field(gt=0)]
class Product(BaseModel):
price: PositiveInt
非常常见。
浙公网安备 33010602011771号