如何访问 typing.Generic 的类型参数?

2025-02-27 09:07:00
admin
原创
69
摘要:问题描述:该typing模块为泛型类型提示提供了一个基类:typing.Generic类。接受方括号中类型参数的子类Generic,例如:list_of_ints = typing.List[int] str_to_bool_dict = typing.Dict[str, bool] 我的问题是,我如何访问这...

问题描述:

typing模块为泛型类型提示提供了一个基类:typing.Generic类。

接受方括号中类型参数的子类Generic,例如:

list_of_ints = typing.List[int]
str_to_bool_dict = typing.Dict[str, bool]

我的问题是,我如何访问这些类型参数?

也就是说,str_to_bool_dict作为输入,我如何获得strbool作为输出?

基本上我正在寻找这样的功能

>>> magic_function(str_to_bool_dict)
(<class 'str'>, <class 'bool'>)

解决方案 1:

Python >= 3.8

从 Python3.8 开始有typing.get_args

print( get_args( List[int] ) ) # (<class 'int'>,)

PEP-560还提供了,它允许我们使用第 n个泛型基数__orig_bases__[n]的参数:

from typing import TypeVar, Generic, get_args

T = TypeVar( "T" )

class Base( Generic[T] ):
    pass

class Derived( Base[int] ):
    pass

print( get_args( Derived.__orig_bases__[0] ) ) # (<class 'int'>,)

Python >= 3.6

从 Python 3.6 开始,有一个公共的__args__and ( __parameters__) 字段。例如:

print( typing.List[int].__args__ )

它包含泛型参数(即int),同时__parameters__包含泛型本身(即~T)。


Python < 3.6

使用typing_inspect.getargs


一些注意事项

typing遵循PEP8。 PEP8 和typing均由 Guido van Rossum 共同撰写。 双前下划线和尾下划线在 中定义为: “位于用户控制的命名空间中的“魔法”对象或属性”

双下划线部分也进行了内联注释;从官方的打字存储库中我们可以看到:

  • __args__是下标中使用的所有参数的元组,例如Dict[T, int].__args__ == (T, int)”。

然而,作者还指出:

  • “type 模块处于临时状态,因此它不受向后兼容性的高标准保护(尽管我们尝试尽可能地保留它),对于(尚未记录的)dunder 属性尤其如此__union_params__。如果您想在运行时上下文中使用 typing 类型,那么您可能会对该typing_inspect项目感兴趣(其中的一部分可能最终会在以后的 typing 中出现)。”

一般来说,无论你做什么,typing都需要暂时保持最新状态。如果你需要向前兼容的更改,我建议你编写自己的注释类。

解决方案 2:

据我所知,这里没有令人满意的答案。

我想到的是__args__存储此信息的未记录的属性:

list_of_ints.__args__
>>>(<class 'int'>,)

str_to_bool_dict.__args__
>>>(<class 'str'>, <class 'bool'>)

但模块的文档中没有提及它typing

值得注意的是,它几乎在文档中被提及:

也许我们还应该讨论是否需要记录 的所有关键字参数GenericMeta.__new__。有tvarsargsoriginextraorig_bases。我认为我们可以谈谈前三个(它们对应于__parameters____args__、 和__origin__,这些在打字中被大多数东西使用)。

但它并没有成功:

我根据问题中的讨论添加了和文档字符串。我决定不在文档字符串中描述GenericMeta和朋友__all__。相反,我只是在它们第一次使用的地方添加了一条注释。GenericMeta`GenericMeta.__new__`__origin__

从那里开始,你仍然有三个非互斥的选择:

  • 等待typing模块完全成熟,并希望这些功能能够很快被记录下来

  • 加入Python 创意邮件列表,看看是否可以收集足够的支持以使这些内部功能公开/成为 API 的一部分

  • 同时处理未记录的内部结构,赌这些结构不会发生改变或者改变很小。

请注意,第三点几乎无法避免,因为API 也可能会发生变化:

打字模块已临时包含在标准库中。如果核心开发人员认为有必要,可能会添加新功能,并且 API 甚至可能在次要版本之间发生变化

解决方案 3:

看来这个内部方法可以解决问题

typing.List[int]._subs_tree()

返回元组:

(typing.List, <class 'int'>)

但这是一个私有 API,可能有更好的答案。

解决方案 4:

问题专门询问了typing.Generic,但事实证明(至少在typing模块的早期版本中)并非所有可下标类型都是 的子类Generic。在较新的版本中,所有可下标类型都将其参数存储在__args__属性中:

>>> List[int].__args__
(<class 'int'>,)
>>> Tuple[int, str].__args__
(<class 'int'>, <class 'str'>)

然而,在 Python 3.5 中,一些类(如typing.Tupletyping.Uniontyping.Callable它们存储在不同的属性中,如__tuple_params____union_params__或通常存储在中__parameters__。为了完整起见,这里有一个函数,它可以从任何 Python 版本中的任何可下标类型中提取类型参数:

import typing


if hasattr(typing, '_GenericAlias'):
    # python 3.7
    def _get_base_generic(cls):
        # subclasses of Generic will have their _name set to None, but
        # their __origin__ will point to the base generic
        if cls._name is None:
            return cls.__origin__
        else:
            return getattr(typing, cls._name)
else:
    # python <3.7
    def _get_base_generic(cls):
        try:
            return cls.__origin__
        except AttributeError:
            pass

        name = type(cls).__name__
        if not name.endswith('Meta'):
            raise NotImplementedError("Cannot determine base of {}".format(cls))

        name = name[:-4]
        try:
            return getattr(typing, name)
        except AttributeError:
            raise NotImplementedError("Cannot determine base of {}".format(cls))


if hasattr(typing.List, '__args__'):
    # python 3.6+
    def _get_subtypes(cls):
        subtypes = cls.__args__

        if _get_base_generic(cls) is typing.Callable:
            if len(subtypes) != 2 or subtypes[0] is not ...:
                subtypes = (subtypes[:-1], subtypes[-1])

        return subtypes
else:
    # python 3.5
    def _get_subtypes(cls):
        if isinstance(cls, typing.CallableMeta):
            if cls.__args__ is None:
                return ()

            return cls.__args__, cls.__result__

        for name in ['__parameters__', '__union_params__', '__tuple_params__']:
            try:
                subtypes = getattr(cls, name)
                break
            except AttributeError:
                pass
        else:
            raise NotImplementedError("Cannot extract subtypes from {}".format(cls))

        subtypes = [typ for typ in subtypes if not isinstance(typ, typing.TypeVar)]
        return subtypes


def get_subtypes(cls):
    """
    Given a qualified generic (like List[int] or Tuple[str, bool]) as input, return
    a tuple of all the classes listed inside the square brackets.
    """
    return _get_subtypes(cls)

示范:

>>> get_subtypes(List[int])
(<class 'int'>,)
>>> get_subtypes(Tuple[str, bool])
(<class 'str'>, <class 'bool'>)

解决方案 5:

在你的构造上使用.__args__。所以你需要的魔法函数是这样的——

get_type_args = lambda genrc_type: getattr(genrc_type, '__args__')

我的问题是,我如何访问这些类型参数?

在这种情况下——我该如何访问...

使用 Python 强大的内省功能。

即使作为一名非专业程序员,我也知道我正在尝试检查一些东西,这dir是一个类似于终端中的 IDE 的功能。所以之后

>>> import typing
>>> str_to_bool_dict = typing.Dict[str, bool]

我想看看有什么东西能产生你想要的魔力

>>> methods = dir(str_to_bool_dict)
>>> methods
['__abstractmethods__', '__args__', .....]

我看到了太多信息,为了确定我是否正确,我核实了一下

>>> len(methods)
53
>>> len(dir(dict))
39

现在让我们找到专门为泛型类型设计的方法

>>> set(methods).difference(set(dir(dict)))
{'__slots__', '__parameters__', '_abc_negative_cache_version', '__extra__',
'_abc_cache', '__args__', '_abc_negative_cache', '__origin__',
'__abstractmethods__', '__module__', '__next_in_mro__', '_abc_registry',
'__dict__', '__weakref__'}

其中,、、__parameters____extra__听起来很有帮助。如果没有 self 和 就无法工作,所以我们只剩__args__下和。__origin__`__extra____origin__parameters`__args__

>>> str_to_bool_dict.__args__
(<class 'str'>, <class 'bool'>)

答案由此而来。


自省允许py.testassert语句使 JUnit 派生的测试框架看起来过时。即使是 JavaScript / Elm / Clojure 等语言也没有像 Python 那样直截了当的东西dir。Python 的命名约定允许您无需实际阅读(在某些情况下需要理解)文档即可发现该语言。

因此,请使用内省法进行搜寻,并阅读文档/邮件列表来确认您的发现。

PS 致 OP -- 此方法也回答了您的问题:检查对象是否为 typing.Generic 的正确方法是什么?如果您无法提交邮件列表或您是忙碌的开发人员,请使用发现 - 这是在 python 中执行此操作的方法。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2974  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1836  
  PLM(产品生命周期管理)系统在企业的产品研发、生产与管理过程中扮演着至关重要的角色。然而,在实际运行中,资源冲突是经常会遇到的难题。资源冲突可能导致项目进度延迟、成本增加以及产品质量下降等一系列问题,严重影响企业的效益与竞争力。因此,如何有效应对PLM系统中的资源冲突,成为众多企业关注的焦点。接下来,我们将详细探讨5...
plm项目管理系统   47  
  敏捷项目管理与产品生命周期管理(PLM)的融合,正成为企业在复杂多变的市场环境中提升研发效率、增强竞争力的关键举措。随着技术的飞速发展和市场需求的快速更迭,传统的研发流程面临着诸多挑战,而将敏捷项目管理理念融入PLM,有望在2025年实现研发流程的深度优化,为企业创造更大的价值。理解敏捷项目管理与PLM的核心概念敏捷项...
plm项目   47  
  模块化设计在现代产品开发中扮演着至关重要的角色,它能够提升产品开发效率、降低成本、增强产品的可维护性与可扩展性。而产品生命周期管理(PLM)系统作为整合产品全生命周期信息的关键平台,对模块化设计有着强大的支持能力。随着技术的不断发展,到 2025 年,PLM 系统在支持模块化设计方面将有一系列令人瞩目的技术实践。数字化...
plm软件   48  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用