Skip to content

typing.Annotated

typing.Annotated 是 Python 类型系统中一个强大的工具,它允许开发者为类型声明添加额外元数据,这些元数据可以被库、框架或自定义代码读取和使用。

什么是 Annotated?

Annotated[T, metadata] 的基本语法表示一个类型 T 和与之关联的元数据。元数据可以是任意Python对象或对象集合。

python
from typing import Annotated

# 基本用法:为字符串类型添加描述性元数据
name: Annotated[str, "首字母必须大写"]

这个注解表明 namestr 类型,并且附带了一个说明:"首字母必须大写"。

核心概念

关键特性

  • 运行时访问:通过 .__metadata__ 属性可以访问附加的元数据
  • 向后兼容:没有特殊处理逻辑的工具会忽略元数据,只将其视为基础类型 T
  • 灵活扩展:元数据可以是任意Python对象,提供了极大的灵活性

实际应用场景

1. FastAPI 数据验证

FastAPI 广泛使用 Annotated 来声明参数验证规则:

python
from fastapi import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(max_length=50)]):
    return {"q": q}

这里 Annotated 告诉 FastAPI 参数 q 应该是字符串类型,并且最大长度不超过50个字符。

优势

使用 Annotated 可以让参数声明更加简洁,避免了过长的函数签名,提高了代码的可读性。

2. Pydantic 模型验证

在 Pydantic 中,Annotated 可以替代繁琐的验证器写法:

python
from typing import Annotated
from pydantic import BaseModel, Field

# 传统方式需要自定义验证器
class OldModel(BaseModel):
    x: int
    # 需要额外的验证器方法...

# 使用 Annotated 的简洁方式
class NewModel(BaseModel):
    x: Annotated[int, Field(lt=20, gt=2)]

这种方式利用 Pydantic 内置的能力来解析 Annotated 中的元数据并自动进行验证。

3. 类型延迟解析

在处理循环依赖时,Annotated 非常有用。Strawberry GraphQL 使用它来实现延迟类型解析:

python
from typing import Annotated
import strawberry

@strawberry.type
class User:
    name: str
    friends: Annotated[list["User"], strawberry.lazy("app.types")]

4. Typer CLI 应用

Typer 框架使用 Annotated 生成命令行帮助信息和参数验证:

python
from typing import Annotated
import typer

def main(name: Annotated[str, typer.Argument(help="你的名字")]):
    print(f"Hello {name}")

if __name__ == "__main__":
    typer.run(main)

5. 自定义元数据处理

你可以创建自己的框架来处理 Annotated 元数据:

python
from typing import Annotated, TypedDict, get_type_hints

def reducer(a: list, b: float) -> list:
    return a + [b]

class State(TypedDict):
    x: Annotated[list, reducer]

# 提取和处理元数据
metadata = {
    name: typ.__metadata__[0]
    for name, typ in get_type_hints(State, include_extras=True).items()
}

最佳实践

注意事项

  • 工具支持Annotated 的效果取决于使用的库是否支持读取元数据
  • 适度使用:不要过度使用复杂的元数据,保持代码的可读性
  • 文档说明:为自定义的元数据使用方式提供清晰的文档
python
from fastapi import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/users/")
async def read_users(
    age: Annotated[int, Query(gt=0, lt=120, description="用户年龄")],
    name: Annotated[str, Query(min_length=1, max_length=50)]
):
    return {"name": name, "age": age}
python
from pydantic import BaseModel, Field
from typing import Annotated

class User(BaseModel):
    username: Annotated[str, Field(min_length=3, max_length=20)]
    email: Annotated[str, Field(pattern=r"^[^@]+@[^@]+\.[^@]+$")]

总结

typing.Annotated 是一个强大的类型注解工具,它通过为类型添加元数据来扩展Python类型系统的表达能力。虽然它本身不提供任何功能,但当与支持它的库(如FastAPI、Pydantic、Strawberry、Typer等)结合使用时,可以显著简化代码并提高开发效率。

在实际开发中,合理使用 Annotated 可以让你的代码更加声明式、简洁和易于维护,特别是在需要复杂验证、文档生成或自定义处理的场景中。