应使用 typing.get_type_hints() 获取运行时类型对象,而非直接访问 __annotations__;后者仅存字符串形式的类型提示,不求值、不处理泛型与前向引用,而 get_type_hints() 自动解析并支持 Annotated 元数据等细节。

直接用 __annotations__ 拿不到运行时的实际类型信息——它只存字符串形式的类型提示,且不求值、不校验、不处理泛型参数,更不会反映实际传入的值。
为什么 __annotations__ 里看到的是字符串而不是类型对象?
Python 3.7+ 默认启用 postponed evaluation(PEP 563),所有类型提示被存为字符串,避免前向引用和导入循环问题。即使你写了 def f(x: List[int]) -> Optional[str]: ...,f.__annotations__ 里 'x' 对应的值是 "List[int]" 这个字符串,不是 typing.List[int] 类型。
- 除非显式启用
from __future__ import annotations(默认已生效),否则旧写法可能触发运行时导入失败 -
eval()手动解析有风险:依赖当前命名空间、不处理嵌套泛型、无法还原Self或Literal等特殊类型 -
typing.get_type_hints()是官方推荐替代方案,它会自动解析字符串并处理前向引用
如何安全获取可使用的类型对象?用 typing.get_type_hints()
它才是真正用于运行时消费类型提示的函数,会返回一个 dict,键为参数名或 'return',值为解析后的类型对象(如 int、list、typing.Union[str, None])。
- 必须传入函数对象本身,例如
get_type_hints(my_func),不能传my_func.__annotations__ - 支持
include_extras=True(Python 3.11+)来保留Annotated中的元数据 - 遇到未定义名称(如前向引用类名)时,默认会尝试在函数定义所在模块的全局命名空间中查找
- 若函数带
__future__.annotations,且类型中有未解析的字符串(如"MyClass"),get_type_hints能正确处理;而直接读__annotations__就只能拿到原始字符串
__annotations__ 还有什么实用场景?
它适合做静态分析、文档生成、IDE 提示等不依赖真实类型的场景,或者需要原始声明结构的元编程。
立即学习“Python免费学习笔记(深入)”;
- 检查是否声明了某个参数的类型:
'timeout' in func.__annotations__ - 提取原始字符串做 AST 分析或生成 OpenAPI schema(此时字符串比运行时类型更贴近源码意图)
- 配合
dataclasses.field()的type推导逻辑(内部仍走get_type_hints,但字段定义阶段可用__annotations__做轻量 fallback) - 注意:类体中的
__annotations__不包含继承来的注解,也不包含方法注解——那是方法自身的属性
真正要对类型做判断、转换或校验时,别碰 __annotations__;该用 typing.get_type_hints() 就用,它才是设计来干这事的。泛型擦除、前向引用、Annotated 处理这些细节,都藏在它背后,手动模拟很容易漏掉边界情况。


















