__annotations__ 不能直接用作运行时类型信息,因为它仅存储字符串或未求值的类型表达式,不解析泛型、前向引用、类型别名;应使用 typing.get_type_hints() 安全转换。

不能直接用 __annotations__ 获取运行时类型信息,它只存字符串或未求值的类型表达式,且不处理泛型、前向引用或类型别名。
为什么 __annotations__ 不能当运行时类型用
__annotations__ 是类或函数定义时静态收集的字典,内容取决于 Python 版本和是否启用了 from __future__ import annotations:
- Python 3.7+ 开启
annotations后,所有注解都以字符串形式存入,比如Dict[str, int]不会被解析,只是字符串"Dict[str, int]" - 即使没开启,泛型如
list[int](3.9+)也仅在运行时构造出参数化类型对象,但__annotations__里仍是原始 AST 表达式,不是可检查的类型对象 - 前向引用(如
"User")、字符串字面量、typing.ForwardRef都不会被自动解析成真实类型 - 类型别名(如
UserID = int)在__annotations__中仍为UserID,不会展开
如何安全地把 __annotations__ 转成可检查的类型对象
需要手动调用 typing.get_type_hints(),它会做三件事:解析字符串、处理前向引用、展开局部/全局命名空间。这是唯一推荐路径:
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
from typing import get_type_hints
<p>class User:
name: str
age: "int" # 前向引用字符串
tags: list[str] # Python 3.9+ 泛型</p><h1>直接读 <strong>annotations</strong> → {'name': <class>, 'age': 'int', 'tags': 'list[str]'}</class>
</h1><h1>用 get_type_hints → {'name': <class>, 'age': <class>, 'tags': list[str]}</class></class>
</h1>
- 必须传入目标对象(类或函数),不能只传
__annotations__字典 - 默认使用定义处的
globals()和locals();若类在函数内定义或有嵌套作用域,需显式传入localns - 遇到无法解析的名称(如拼错的类名),默认抛
NameError,可加include_extras=True保留Annotated等元数据
获取类字段类型时,别漏掉继承链和 __dataclass_fields__
__annotations__ 只包含当前类直接写的注解,不合并父类,也不包含 dataclass 自动生成的字段:
- 对普通类:用
get_type_hints(cls)本身不合并父类;需手动遍历cls.__mro__并累加(注意顺序和覆盖逻辑) - 对
@dataclass类:字段可能来自__annotations__,也可能来自field(default=...)且无注解,此时得查cls.__dataclass_fields__的type属性 - 对
TypedDict或NamedTuple子类:它们的字段信息存在__annotations__,但语义不同,get_type_hints()仍可用,不过要留意total=False等行为
常见报错和绕过方式
典型错误是 NameError: name 'XXX' is not defined,尤其在模块级未定义、循环导入或动态生成类时:
- 在
get_type_hints()中传globalns={}或补全缺失名称,例如globalns={'Optional': typing.Optional, 'Union': typing.Union} - 用
try/except NameError捕获并 fallback 到字符串原值(仅用于日志或调试,不可用于类型检查) - 避免在
__init_subclass__中过早调用get_type_hints()——此时子类可能还未完全构建,__annotations__尚未就绪 -
Literal['a', 'b']这类字面量类型在旧版 Python(get_type_hints() 支持,需升级或手动处理
真正要用类型做运行时判断(比如序列化、校验、依赖注入),永远以 get_type_hints() 输出为准,而不是直接读 __annotations__;而它的健壮性高度依赖上下文环境是否完整——这点最容易被忽略。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










