Python 的函数注解可以为参数和返回值附加元数据。注解经常被用作类型提示,帮助编辑器、类型检查器、框架和其他工具理解代码中的数据类型。
Python 运行时通常不会主动强制执行类型提示。具体是否进行检查、转换或校验,取决于使用这些注解的工具或框架。相关类型及规则可参阅 Python 官方的 typing 文档。
参数与返回值注解
参数注解写在参数名之后,返回值注解写在 -> 之后:
def search(query: str, top_k: int = 4) -> list[str]:
return [query] * top_k
这里:
query: str表示query预期为字符串;top_k: int表示top_k预期为整数;-> list[str]表示函数预期返回字符串列表;= 4是参数默认值,不是类型注解的一部分。
变量也可以使用注解:
request_id: str
retry_count: int = 0
常见类型表达式
内置容器可以声明元素类型:
names: list[str]
scores: dict[str, float]
coordinates: tuple[float, float]
联合类型 X | Y 表示值可以属于多个类型之一:
identifier: int | str
X | None 表示值也可以是 None:
character_id: str | None = None
“允许为 None”和“调用时可以省略”不是同一件事。类型表达式决定允许的值,默认值决定参数是否可以不传:
def find(name: str | None): # 必须传参,但可以传 None
...
def find(name: str | None = None): # 可以省略
...
Annotated
typing.Annotated 可以在一个类型之外附加由特定工具解释的元数据:
from typing import Annotated
UserName = Annotated[str, "non-empty user name"]
其中 str 仍然是基础类型,后面的值是附加元数据。不认识这些元数据的类型检查器通常仍会把它当作 str;认识它的框架则可以赋予元数据特定含义。
例如,FastAPI 的依赖注入可以把 Annotated 与 Depends 组合:
from typing import Annotated
from fastapi import Depends
async def require_user() -> User:
...
async def profile(
user: Annotated[User, Depends(require_user)],
):
return user
这里的 User 描述参数类型,Depends(require_user) 则告诉 FastAPI 如何获得这个参数。
运行时边界
Python 官方说明,运行时本身不强制检查函数和变量的类型注解。下面的调用不一定会因为参数类型不匹配而被 Python 自动拒绝:
def double(value: int) -> int:
return value * 2
double("a")
静态类型检查器可以在运行前发现部分问题;Pydantic、FastAPI 等工具也可以在运行时读取注解并执行自己的校验或转换。因此,类型提示描述的是类型意图,实际运行时行为仍取决于函数实现和使用注解的工具。