我试图找到一种可靠/跨版本(3.5+)的方法来检查类型注释是否是给定泛型类型的“子类”(即从类型注释对象中获取泛型类型)。
在 Python 3.5 / 3.6 上,它可以轻而易举地工作,正如您所期望的:
>>> from typing import List
>>> isinstance(List[str], type)
True
>>> issubclass(List[str], List)
True
在 3.7 上,看起来泛型类型的实例不再是 的实例type
,所以它会失败:
>>> from typing import List
>>> isinstance(List[str], type)
False
>>> issubclass(List[str], List)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.7/typing.py", line 716, in __subclasscheck__
raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks
想到的其他想法是检查实际的实例类型,但是:
Python 3.6 / 3.5:
>>> type(List[str])
<class 'typing.GenericMeta'>
蟒蛇 3.7:
>>> type(List[str])
<class 'typing._GenericAlias'>
但这并没有真正进一步表明哪个是实际的泛型类型(可能不是 List);此外,以这种方式进行检查感觉非常错误,特别是因为_GenericAlias
now 变成了“私有”类型(注意下划线)。
可以检查的另一件事是__origin__
关于类型的参数,但这也不是正确的方法。
它在 3.7 上仍然不同:
>>> List[str].__origin__
<class 'list'>
而 3.5 / 3.6:
>>> List[str].__origin__
typing.List
我一直在寻找这样做的“正确”方式,但在 Python 文档/谷歌搜索中没有找到它。
现在,我假设必须有一种干净的方式来进行这种检查,因为像 mypy 这样的工具会依赖它来进行类型检查..?
更新:关于用例
好的,在这里添加更多上下文..
因此,我的用例是对函数签名(参数类型/默认值、返回类型、文档字符串)使用自省来自动为它们生成 GraphQL 模式(从而减少样板的数量)。
对于这是否是一个好主意,我仍然有点担心。
从可用性的角度来看,我喜欢它(无需学习另一种声明函数签名的方法:只需以通常的方式注释您的类型);请参阅此处的两个代码示例以了解我的意思:https ://github.com/rshk/pyql
我想知道以这种方式使用类型支持泛型类型(列表、字典、联合......)是否typing
会增加太多“黑魔法”,这可能会以意想不到的方式破坏。(目前这不是一个大问题,但是未来的 Python 版本,超过 3.7 版本呢?这会成为维护的噩梦吗?)。
当然,替代方法是只使用支持更可靠/面向未来的检查的自定义类型注释,例如:https://github.com/rshk/pyql/blob/master/pyql/schema/types/core。 py#L337-L339
..但不利的一面是,这将迫使人们记住他们必须使用自定义类型注释。此外,我不确定 mypy 将如何处理(我认为需要在某处声明自定义类型与typing.List
.. 完全兼容?听起来仍然很骇人听闻)。
(我主要是在寻求有关这两种方法的建议,最重要的是我可能错过的两种选择的任何优点/缺点。希望这不会变得“太宽泛”。)。