我试图找到一种可靠的/交叉版本(3.5+)方法来检查类型批注是否是给定泛型类型的“子类”(即从类型批注对象中获取泛型类型)。
在Python3.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'>
Python 3.7:
>>> type(List[str])
<class 'typing._GenericAlias'>
但这并不能进一步说明哪种类型是真正的泛型类型(可能不是列表);此外,用这种方式进行检查感觉非常错误,特别是由于
_GenericAlias
现在变成了“私有”类型(注意下划线)。
另一个可以检查的是
__origin__
类型上的参数,但这也不是正确的方式。
但在3.7版中仍然有所不同:
>>> List[str].__origin__
<class 'list'>
而3.5/3.6:
>>> List[str].__origin__
typing.List
我一直在寻找这样做的“正确”方法,但在python docs/google搜索中没有找到。
现在,我假设必须有一个干净的方法来执行这个检查,因为像mypy这样的工具将依赖它来执行类型检查。
更新:关于用例
好的,在这里添加一点上下文。
因此,我的用例是对函数签名(参数类型/默认值、返回类型、docstring)进行自省,以自动为它们生成一个graphql模式(从而减少样板文件的数量)。
我对这是否是个好主意仍有点不知所措。
我从可用性的角度喜欢它(不需要再学习另一种声明函数签名的方法:只需用通常的方法注释您的类型);请参阅下面的两个代码示例来理解我的意思:
https://github.com/rshk/pyql
我想知道是否支持使用
typing
这种方式增加了太多的“黑色魔法”,可能会以意想不到的方式打破。(现在这不是一个大问题,但是3.7以后的Python版本呢?这会成为维护的噩梦吗?.
当然,另一种选择是只使用支持更可靠/将来验证检查的自定义类型注释,例如:
https://github.com/rshk/pyql/blob/master/pyql/schema/types/core.py#L337-L339
…但缺点是,这会迫使人们记住他们必须使用自定义类型注释。此外,我不确定Mypy将如何处理这个问题(我假设需要在某个地方声明自定义类型与
typing.List
…?听起来还是很粗俗)。
(我主要是在征求关于这两种方法的建议,最重要的是,我可能忽略了这两种方法的任何利弊。希望这不会变得“太宽泛”了。