You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:43:23