首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >检查对象是否为typing.Generic的正确方法是什么?

检查对象是否为typing.Generic的正确方法是什么?
EN

Stack Overflow用户
提问于 2018-03-08 10:45:15
回答 6查看 21.4K关注 0票数 55

我试图编写验证类型提示的代码,为了这样做,我必须找出注释是什么类型的对象。例如,考虑下面这个片段,它应该告诉用户所期望的值是什么样的:

代码语言:javascript
复制
import typing

typ = typing.Union[int, str]

if issubclass(typ, typing.Union):
    print('value type should be one of', typ.__args__)
elif issubclass(typ, typing.Generic):
    print('value type should be a structure of', typ.__args__[0])
else:
    print('value type should be', typ)

它应该打印"value type应该是(int,str)之一“,但是它会抛出一个异常:

代码语言:javascript
复制
Traceback (most recent call last):
  File "untitled.py", line 6, in <module>
    if issubclass(typ, typing.Union):
  File "C:\Python34\lib\site-packages\typing.py", line 829, in __subclasscheck__
    raise TypeError("Unions cannot be used with issubclass().")
TypeError: Unions cannot be used with issubclass().

isinstance也不起作用:

代码语言:javascript
复制
>>> isinstance(typ, typing.Union)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python34\lib\site-packages\typing.py", line 826, in __instancecheck__
    raise TypeError("Unions cannot be used with isinstance().")
TypeError: Unions cannot be used with isinstance().

检查typ是否为typing.Generic的正确方法是什么

如果可能的话,我希望看到一个由文档、PEP或其他资源支持的解决方案。通过访问无文档的内部属性来“工作”的A“解决方案”很容易找到。但更有可能的是,它将是一个实现细节,并将在未来的版本中发生变化。我在找“正确的方法”来做这件事。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2021-02-09 10:35:07

正如桑尼-加西亚在评论中指出的那样,get_origin()工作于python3.8

代码语言:javascript
复制
import typing
from typing import get_origin

typ = typing.Union[int, str]
get_origin(typ) == typing.Union
#True

您可以在文档中找到更多详细信息。

票数 14
EN

Stack Overflow用户

发布于 2018-04-28 19:24:56

你可能在找__origin__

* __origin__保留对已订阅类型的引用,

代码语言:javascript
复制
#   e.g., Union[T, int].__origin__ == Union;`

import typing

typ = typing.Union[int, str]

if typ.__origin__ is typing.Union:
    print('value type should be one of', typ.__args__)
elif typ.__origin__ is typing.Generic:
    print('value type should be a structure of', typ.__args__[0])
else:
    print('value type should be', typ)

>>>value type should be one of (<class 'int'>, <class 'str'>)

我所能找到的倡导使用这个无文档属性的最好方法是Guido (日期为2016年)的令人放心的报价

我能推荐的最好的方法是使用__origin__ --如果我们要更改这个属性,仍然必须有其他方法来访问相同的信息,而且对于出现__origin__的情况,grep是很容易的。(与__extra__相比,我不太担心__extra__的变化。)您还可以查看内部函数_gorg()_geqv() (显然,这些名称并不是任何公共API的一部分,但它们的实现非常简单,在概念上非常有用)。

文件中的这一警告似乎表明,目前还没有在大理石上设置任何东西:

如果核心开发人员认为有必要的话,可能会添加新的特性,API甚至在小版本之间也会发生变化。

票数 45
EN

Stack Overflow用户

发布于 2018-10-05 11:22:07

没有官方的途径来获取这些信息。typing模块仍在大量开发中,没有公共API可供使用。(事实上,它可能永远也不会有。)

我们所能做的就是查看模块的内部结构,找出最简单的方法来获取我们想要的信息。而且,由于该模块仍在进行工作,其内部结构将发生变化。很多。

在python3.5和3.6中,泛型具有一个__origin__属性,该属性包含对原始泛型基类的引用(即List[int].__origin__应该是List),但在3.7中进行了更改。现在,确定某物是否是泛型的最简单的方法可能是检查它的__parameters____args__属性。

下面是一组可用于检测泛型的函数:

代码语言:javascript
复制
import typing


if hasattr(typing, '_GenericAlias'):
    # python 3.7
    def _is_generic(cls):
        if isinstance(cls, typing._GenericAlias):
            return True

        if isinstance(cls, typing._SpecialForm):
            return cls not in {typing.Any}

        return False


    def _is_base_generic(cls):
        if isinstance(cls, typing._GenericAlias):
            if cls.__origin__ in {typing.Generic, typing._Protocol}:
                return False

            if isinstance(cls, typing._VariadicGenericAlias):
                return True

            return len(cls.__parameters__) > 0

        if isinstance(cls, typing._SpecialForm):
            return cls._name in {'ClassVar', 'Union', 'Optional'}

        return False
else:
    # python <3.7
    if hasattr(typing, '_Union'):
        # python 3.6
        def _is_generic(cls):
            if isinstance(cls, (typing.GenericMeta, typing._Union, typing._Optional, typing._ClassVar)):
                return True

            return False


        def _is_base_generic(cls):
            if isinstance(cls, (typing.GenericMeta, typing._Union)):
                return cls.__args__ in {None, ()}

            if isinstance(cls, typing._Optional):
                return True

            return False
    else:
        # python 3.5
        def _is_generic(cls):
            if isinstance(cls, (typing.GenericMeta, typing.UnionMeta, typing.OptionalMeta, typing.CallableMeta, typing.TupleMeta)):
                return True

            return False


        def _is_base_generic(cls):
            if isinstance(cls, typing.GenericMeta):
                return all(isinstance(arg, typing.TypeVar) for arg in cls.__parameters__)

            if isinstance(cls, typing.UnionMeta):
                return cls.__union_params__ is None

            if isinstance(cls, typing.TupleMeta):
                return cls.__tuple_params__ is None

            if isinstance(cls, typing.CallableMeta):
                return cls.__args__ is None

            if isinstance(cls, typing.OptionalMeta):
                return True

            return False


def is_generic(cls):
    """
    Detects any kind of generic, for example `List` or `List[int]`. This includes "special" types like
    Union and Tuple - anything that's subscriptable, basically.
    """
    return _is_generic(cls)


def is_base_generic(cls):
    """
    Detects generic base classes, for example `List` (but not `List[int]`)
    """
    return _is_base_generic(cls)


def is_qualified_generic(cls):
    """
    Detects generics with arguments, for example `List[int]` (but not `List`)
    """
    return is_generic(cls) and not is_base_generic(cls)

所有这些函数都应该在所有python版本的<= 3.7中工作(包括任何使用typing模块后端的<3.5 )。

票数 21
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49171189

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档