FastAPI之 Python的类型提示

类型提示

Python语言通过标准库的typing模型,提供类型提示机制,但是Python解释器不理会这些类型信息的,实际运行时候,相当于这些信息是不存在的。所以即便你传递了一个错误类型,编译器也不会报错(有些工具可以给些提示)。

Python开发者是可以看到这些提示信息的,利用这些信息可以实现更多功能,比如检查类型错误。Pydantic包就是这样的软件包。Pydantic和FastAPI结合,能够大幅简化Web开发工作中许多事物。

类型提示可激活IDE的代码补全功能。

类型信息提示具体由三种写法:

  1. 针对变量
  2. 函数形参
  3. 函数返回值,

  • 基本标量类型

在变量名后加冒号例如 name: type name: type = value

复制代码
x: int, y: int
  • 容器类型

变量如果是集合类型,其中部件也可以做类型注解。比如 name:dict[keytype,valuetype]={key1:val1,key2:val2}

复制代码
nums: List[int], mapping: Dict[str, float]

大小写等价,即list[int] 等同于List[int]

  • 可选&默认

    def greet(name: str | None = None) -> str:

  • 不定长参数

    def log(msg: str, *args: int, **kw: float) -> None:

  • Any (typing)表示任何一种类型

    def a(x: Any) -> int:

  • Union 表示类型中某一种,Union[str,int],表示str或者int

    def u(x: Union[int, str]) -> int:

  • 限制类型范围(TypeVar 边界)

python 复制代码
Num = TypeVar('Num', int, float) # 指定为int  float 这2种
def add(a: Num, b: Num) -> Num:

变量为空的三种写法

python 复制代码
from typing import Union, Optional


def example1(var : Union[float,None]):
    pass

def example2(var : Optional[float,None]):
    pass

def example3(var : float|None):
    pass

基本语法:在Python中声明可为空变量的三种等价写法:

  • Union[float, None]:明确指出联合类型
  • Optional[float]:更简洁的写法,等同于Union[float,None]
  • float | None:Python 3.10引入的现代写法

为类型添加额外信息

Annotated 是Python的typing模块中的一个工具,用来为类型添加元数据或附加信息。

通常是给类型贴便签 ------解释器不管,静态工具/运行时库可以读,用来做单位、校验、依赖注入等元数据。typing.Annotated[type, *metadata]

  • 基本用法:Annotated[Type,metadata1,metadata2,...]
  • Type: 这里是被注解的类型。比如int str List[int]等
  • metadata1,metadata2,...这些是附加类型元数据,可以是任何表达式,通常是一些类或字符串,用于提供附加信息。

例如:

直接从函数种读取元数据

python 复制代码
from typing import Annotated

def log_usage(func_name: str, param: Annotated[int, "用户ID"]):
    print(f"[{func_name}] 用户 {param} 被访问")

# 静态工具或框架可以读取这个元数据
print(log_usage.__annotations__)
# {'func_name': <class 'str'>, 'param': typing.Annotated[int, '用户ID']}

通过typing.get_type_hints获取类型提示

python 复制代码
import typing
Age = typing.Annotated[int, "this is an age value"]

def set_age(age: Age):
    print(f"set age to {age}")

Age = Annotated[int,"this is an age value"] 表示一个整数类型int,附加一个字符串元数据。

元数据不影响int基本行为,但可以用于文档或类型检查工具。

运行时可以把标签获取出来

typing.get_type_hints(..., include_extras=True) 返回带 __metadata__ 的构造:

python 复制代码
hints = typing.get_type_hints(set_age, include_extras=True)
print(hints['age'])
print(hints['age'].__metadata__)  

以上代码打印结果:

python 复制代码
typing.Annotated[int, 'this is an age value']
('this is an age value',)

第三方库直接给你封装好:

  • pydantic ≥1.10 自动识别 Annotated[..., Field(...)]

  • FastAPI ≥0.95 用 Annotated[Type, Depends(...)] 代替旧版 Depends

  • typerAnnotated[str, typer.Argument(help='xxx')]

  • typeguardbeartype 支持 Annotated[str, IsRegex] 等自定义约束


使用 Annotated 元数据

例子:用 pydantic 做"区间 + 单位"校验

python 复制代码
from typing import Annotated
from pydantic import BaseModel, Field, conint

class Config:
    # 1. 用 Field 写约束
    timeout: Annotated[int, Field(gt=0, le=30, description="seconds")]

    # 2. 用 conint 也行
    retry: Annotated[conint(ge=1, le=5), "max retry"]

cfg = Config(timeout=15, retry=3)

pydantic 只认 Field/conint 这类自己定义的 metadata,普通字符串当文档。


例子:FastAPI 0.95+ 全新依赖注入写法

python 复制代码
from fastapi import FastAPI, Depends, Annotated

app = FastAPI()

async def get_current_user(token: str) -> User:
    ...

@app.get("/whoami")
def whoami(user: Annotated[User, Depends(get_current_user)]):
    return {"name": user.name}

例子:自己写装饰器 把函数元数据拿出来运行

python 复制代码
from typing import Annotated
import inspect

# 1. 定义一个"校验函数"
def validate_positive(x: int) -> bool:
    if x <= 0:
        raise ValueError("必须为正数")
    return True

# 2. 把函数当作元数据贴上去
def deposit(
    amount: Annotated[int, validate_positive]   # ← 函数当元数据
) -> str:
    return f"存入 {amount} 元"

# 3. 手动把元数据取出来并调用
sig = inspect.signature(deposit)
param = sig.parameters['amount']
validator = param.annotation.__metadata__[0]   # 拿到 validate_positive
validator(100)        # 正常
validator(-10)        # 触发 ValueError

运行结果:

ValueError: 必须为正数

→ 你看到:元数据是函数,但除非你显式拿它调用,否则什么也不会发生


例子:借助第三方库 让框架自动帮你调用

typeguard 的下一个版本(≥ 4.2)已支持把 Annotated 里的函数当成运行时校验器

这里用 annotated-types + pydantic 演示一种社区通行做法:

python 复制代码
from typing import Annotated
from annotated_types import Gt

def _not_zero(x: int) -> int:
    if x == 0:
        raise ValueError("不能为 0")
    return x

# 把函数包成"类型适配器"
NotZero = Annotated[int, _not_zero]

from pydantic import BaseModel, TypeAdapter

ta = TypeAdapter(NotZero)
ta.validate_python(5)   # 正常
ta.validate_python(0)   # ValueError: 不能为 0

函数元数据被 Pydantic 的 TypeAdapter 取出来并执行。


例子:自己写装饰器消费 metadata

python 复制代码
from typing import Annotated, get_args, get_origin
import inspect

def _check_range(val, meta):
    for m in meta:
        if hasattr(m, 'min') and not (m.min <= val <= m.max):
            raise ValueError(f'{val} out of range [{m.min}, {m.max}]')

def validate(func):
    sig = inspect.signature(func)
    hints = typing.get_type_hints(func, include_extras=True)
    for name, param in sig.parameters.items():
        annotation = hints[name]
        if get_origin(annotation) is Annotated:
            typ, *meta = get_args(annotation)
            # 把元数据存起来,调用时检查
            param._meta = meta
    def wrapper(*args, **kwargs):
        bound = sig.bind(*args, **kwargs)
        for name, val in bound.arguments.items():
            meta = getattr(sig.parameters[name], '_meta', ())
            _check_range(val, meta)
        return func(*args, **kwargs)
    return wrapper

class ValueRange:
    def __init__(self, min, max):
        self.min, self.max = min, max

@validate
def set_age(age: Annotated[int, ValueRange(0, 120)]):
    print("age =", age)

set_age(150)   # ValueError: 150 out of range [0, 120]
相关推荐
刘逸潇20055 小时前
中间件与CORS(基于fastapi)
中间件·fastapi
hello kitty w5 小时前
Python学习(11) ----- Python的泛型
windows·python·学习
没有梦想的咸鱼185-1037-16635 小时前
AI Agent结合机器学习与深度学习在全球气候变化驱动因素预测中的应用
人工智能·python·深度学习·机器学习·chatgpt·数据分析
测试19986 小时前
Selenium自动化测试+OCR-获取图片页面小说详解
自动化测试·软件测试·python·selenium·测试工具·ocr·测试用例
闲人编程6 小时前
使用MLflow跟踪和管理你的机器学习实验
开发语言·人工智能·python·机器学习·ml·codecapsule
panplan.top6 小时前
Tornado + Motor 微服务架构(Docker + 测试 + Kubernetes)
linux·python·docker·微服务·k8s·tornado
vipbic6 小时前
使用Cursor开发Strapi5插件bag-strapi-plugin
前端·ai编程·cursor
看兵马俑的程序员7 小时前
RAG实现-本地PDF内容加载和切片
开发语言·python·pdf
是梦终空8 小时前
计算机毕业设计240—基于python+爬虫+html的微博舆情数据可视化系统(源代码+数据库)
爬虫·python·pandas·课程设计·毕业论文·计算机毕业设计·微博舆情可视化