使用 Python 3.12.4 运行以下代码:
from typing import Generic, TypeVar
T = TypeVar("T")
class Foo(Generic[T]):
def some_method(self) -> T:
pass
isinstance(Foo[int](), Foo[int])
它会抛出一个
TypeError: Subscripted generics cannot be used with class and instance checks
。
Traceback (most recent call last):
File "/path/to/a.py", line 9, in <module>
isinstance(Foo[int](), Foo[int])
File "/path/to/.pyenv/versions/3.12.4/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/typing.py", line 1213, in __instancecheck__
return self.__subclasscheck__(type(obj))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/.pyenv/versions/3.12.4/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/typing.py", line 1216, in __subclasscheck__
raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks
Python 不允许
isinstance
检查 Generic
s 的理由是什么?
在运行时,类型参数不可验证。这种关系仅在类型检查时检查:
请注意,[作为示例给出的变量]的运行时类型(类)仍然只是
–Node
和Node[int]
是可区分的类对象,但通过实例化它们创建的对象的运行时类并不相同记录区别。这种行为称为“类型擦除”;这是具有泛型的语言(例如 Java、TypeScript)中的常见做法。Node[str]
例如,如果
isinstance(..., list[str])
要在isintansce(..., list)
处处理,结果会非常混乱:
>>> a = [1, 2, 3]
>>> isinstance(a, list[str])
True
还要考虑类型参数不具体的情况:
def is_list_of(v: Any, element_type: type[T]) -> None:
isinstance(v, list[T]) # ???
自 PEP 484 以来,情况一直如此,尽管不是很明确(Callable 和 Generics 是两个不同的部分,并且此注释的含义似乎与我们所理解的略有不同)是,尽管概念上是相同的):
[...]但是,有一些运行时类型检查器可以完成这种繁重的工作,例如 Pydantic。
isinstance(x, typing.Callable[...])
不支持。
历史上,PEP 484 通知是在
此提交中添加的,并且与类型擦除相关的问题在此 GitHub 问题中讨论。由于性能问题,GvR 和其他贡献者似乎最初反对 Node[int]()
(随后它在运行时工作,但速度非常慢,并且被 Mypy 禁止)。