
Python 的泛型在运行时会被擦除,但通过 __orig_class__ 属性可访问实例化时传入的具体类型参数;需注意该属性仅在对象初始化完成后可用,不能在 __init__ 中直接用于类型验证。
python 的泛型在运行时会被擦除,但通过 `__orig_class__` 属性可访问实例化时传入的具体类型参数;需注意该属性仅在对象初始化完成后可用,不能在 `__init__` 中直接用于类型验证。
在 Python 3.12+(尤其是 3.13)中,typing.Generic 的运行时类型反射能力发生了重要变化:__orig_bases__ 已被弃用或不可靠,而 __orig_class__ 成为获取泛型实参的唯一可靠途径——但它仅在实例创建完成之后才被设置,因此无法在 __init__ 或 __new__ 中安全读取。
以下是一个正确、健壮的实现方式:
from typing import Optional, Type, get_origin, get_args
class Test[T]:
data: Optional[T]
def __init__(self, data: Optional[T] = None):
self.data = data
def get_type(self) -> Type[T]:
"""
获取泛型参数 T 在实例化时的具体类型(如 int、str、None 等)。
注意:必须在实例已创建后调用(即不能在 __init__ 内部使用)。
"""
orig_class = getattr(self, "__orig_class__", None)
if orig_class is None:
raise RuntimeError("Cannot access __orig_class__: instance not properly parameterized.")
args = get_args(orig_class)
if not args:
raise TypeError(f"No type arguments found in {orig_class}")
t_arg = args[0]
# 处理 None 类型:get_args(Test[None]) 返回 (type(None),),需保留为 type(None)
return t_arg
def validate_data(self) -> bool:
"""基于运行时推导出的 T 类型,验证 self.data 是否符合预期类型"""
try:
expected_type = self.get_type()
# isinstance(None, type(None)) → True;isinstance(42, type(None)) → False
return isinstance(self.data, expected_type)
except (RuntimeError, TypeError):
return False
# ✅ 正确用法示例
if __name__ == "__main__":
assert Test[int](data=1).validate_data() # True
assert Test[str](data="hello").validate_data() # True
assert not Test[int](data="wrong").validate_data() # False
assert Test[type(None)](data=None).validate_data() # True(显式写 type(None) 更清晰)
# 注意:Test[None] 是语法糖,等价于 Test[type(None)]⚠️ 关键注意事项:
-
__orig_class__是 CPython 实现的私有属性,虽稳定但未被正式纳入 PEP 560/695 规范,应视为“约定俗成”的运行时接口; - 不要尝试在
__init__中调用self.get_type()并立即用于数据校验——此时__orig_class__尚未注入,会触发AttributeError; - 对于
None类型,推荐显式使用type(None)(如Test[type(None)]),避免歧义;Test[None]虽可解析,但部分工具链可能处理不一致; - 若需更严格的类型检查(如支持
Union[int, str]、嵌套泛型等),建议结合typing.get_origin()和typing.get_args()递归解析,或使用第三方库如typing-inspect(兼容性更好)或rttc(runtime type checking)。
总之,__orig_class__ 是当前 Python 泛型运行时元编程的基石,合理利用它,即可在保持类型提示优势的同时,实现动态类型感知与运行时验证。
立即学习“Python免费学习笔记(深入)”;


















