我正在尝试找到一种可靠且跨版本(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'>
Python 3.7:
>>> type(List[str])
<class 'typing._GenericAlias'>
但这并没有进一步说明实际的通用类型是哪个(可能不是List);此外,以这种方式进行检查感觉相当错误,特别是因为_GenericAlias
现在成为了“私有”类型(请注意下划线)。
另外一个可以检查的东西是类型的__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
完全兼容..?还是听起来很 hackish)。
(我主要是请求关于这两种方法的建议,以及可能错过的任何优缺点。希望这不会对 SO 来说变得"太广泛"。)
typing
内部的API。只有语法。静态类型检查器处理文本,而不是对象,因此它们不需要将List[str]
作为对象处理。最多,该工具将从标记化输入构建AST。__origin__
是未发布的实现细节(typing.py
中的注释将其称为内部簿记),因此在自己的项目中依赖这些内容是自担风险。 - Martijn Pieterstyping_inspect
库及其处理方式感兴趣。 - jonafatotyping_inspect
,但是在 Python 3.7 上该库也会给你返回<class 'list'>
,而在 Python 3.6 上则会返回typing.List
。并且它还不支持 Python 3.5。 - Martijn Pieterstyping_inspect
的优点在于它是由核心 mypy 贡献者开发的,一旦稳定下来,它可能会成为核心库的一部分。但我认为 OP 想要的目前无法实现,类型提示在 3.5 - 3.7 之间变化太大了。 - Martijn Pieters