__annotations__ 只保存类定义时顶层变量注解,不包含实例赋值、@property、继承或运行时属性,因其在编译期静态提取且不处理动态行为。

不能直接用 __annotations__ 获取实例属性类型,它只反映类体中显式声明的变量注解,且不继承、不包含动态赋值或 @property。
为什么 __annotations__ 看不到实例属性?
__annotations__ 是类定义时解析的字典,只捕获类语句块顶层的变量注解(如 name: str),不跟踪 self.xxx = ... 这类运行时行为。它也不处理 @property、__slots__ 或父类注解的合并。
- 子类不会自动继承父类的
__annotations__(需手动合并) -
__init__中的self.age = 25不会产生任何注解 -
@property的返回类型不会出现在__annotations__中
如何安全提取类层级的注解(含继承)?
需遍历 MRO,逐层收集并去重合并,注意避免覆盖子类更具体的声明:
def get_class_annotations(cls):
annotations = {}
for base in reversed(cls.__mro__):
ann = getattr(base, '__annotations__', {})
# 只添加当前类中定义的,避免被父类同名项覆盖
for k, v in ann.items():
if k not in annotations:
annotations[k] = v
return annotations
<h1>示例</h1><p>class A:
x: int</p><p>class B(A):
y: str
x: float # 覆盖 A.x</p><p>print(get_class_annotations(B)) # {'x': float, 'y': str}
__annotations__ 和 typing.get_type_hints() 有什么区别?
get_type_hints() 更健壮:它会解析字符串字面量(如 "List[int]")、处理前向引用、展开 ClassVar 和 Final,而原始 __annotations__ 返回的是未经求值的原始值(可能为字符串或未解析的 AST 类型)。
立即学习“Python免费学习笔记(深入)”;
- 若注解含字符串(如
name: "User"),__annotations__返回字符串,get_type_hints()返回实际类型 -
get_type_hints(cls)默认会跳过ClassVar,除非传include_extras=True - 对泛型别名(如
Dict[str, int]),get_type_hints()返回标准泛型类型,__annotations__可能返回typing.Dict或dict(取决于 Python 版本)
想获取实例实际属性的类型?得换思路
没有通用机制能推断运行时属性类型——Python 是动态的。可行路径只有三条:
- 靠文档或约定(如所有子类必须实现
_fields: ClassVar[Dict[str, type]]) - 用
dataclasses或pydantic.BaseModel,它们在创建时生成结构化字段信息(如dataclass_fields(cls)) - 结合
__annotations__+__slots__或__dict__快照做启发式推测(不推荐用于关键逻辑)
真正可靠的类型信息,必须来自静态定义位置(类注解、dataclass 字段、pydantic 模型),而不是靠反射实例。


















