typing.get_origin与GenericAlias.__origin__差异及Annotated类型处理咨询
Python泛型类型
__origin__与get_origin的差异及问题解答 背景说明
Python官方文档对GenericAlias.__origin__的定义:
该属性指向未参数化的泛型类,例如
list[int].__origin__ # 返回list
而typing.get_origin的定义则不同:
获取类型的未订阅版本:对于形式为X[Y, Z, ...]的类型对象,返回X。
多数情况下get_origin(cls)等效于getattr(cls, '__origin__', None),但存在例外:
>>> from typing import Annotated, get_origin >>> get_origin(Annotated[int, 3]) typing.Annotated >>> Annotated[int, 3].__origin__ int
问题解答
1. 为何存在两种定义冲突的“origin”?
核心是两者设计目标完全不同:
__origin__是泛型类专属属性,作用是从参数化的泛型实例(比如list[int])回溯到未参数化的原始泛型类(list),只服务于泛型类型体系。get_origin是通用类型构造器工具,定位是解析所有带“订阅语法”(X[Y,...]形式)的类型对象,返回最外层的构造器本身。Annotated本质不是泛型类,而是类型修饰器——它用Annotated[T, ...]的形式给类型T附加元数据,所以get_origin会把Annotated识别为这个订阅结构的“主构造器”;但__origin__在这里的逻辑是指向被修饰的原始类型T,两者语义不同,才出现了看似冲突的结果。
2. 若无需关注typing.Annotated的元数据,仅需获取类型提示,是否有标准方法去除其包装?
有标准方法,推荐结合get_origin和get_args处理,还能递归应对多层Annotated包装的情况:
from typing import Annotated, get_origin, get_args def unwrap_annotated(type_hint): # 循环处理多层Annotated嵌套包装 while get_origin(type_hint) is Annotated: # Annotated的第一个参数就是原始目标类型 type_hint = get_args(type_hint)[0] return type_hint # 测试用例 print(unwrap_annotated(Annotated[int, 3])) # 输出: <class 'int'> print(unwrap_annotated(Annotated[Annotated[str, "desc"], "extra"])) # 输出: <class 'str'>
你提到的if get_origin(cls) is typing.Annotated: cls = cls.__origin__写法能处理单层包装,但用get_args更贴合Annotated的设计规范——官方定义里Annotated的参数结构就是(原始类型, 元数据...),取第一个参数是预期用法,比直接访问__origin__更稳妥。
内容的提问来源于stack exchange,提问作者Aaron
相关产品推荐
相关产品推荐

