为什么isinstance([1, 2, 3], List[str])的结果为true?

11

我正在玩一下新的类型提示/类型模块,使用Python3.5尝试找到一种确认暗示类型是否等于变量实际类型的方法,并发现了一些令我相当惊讶的东西。

>>> from typing import List
>>> someList = [1, 2, 3]
>>> isinstance(someList, List[str])
True

在继续寻找比较变量和它所暗示的类型的方法时,我也尝试了这个:

>>> anotherList = ["foo", "bar"]
>>> type(anotherList) is List[str]
False

有人能解释一下为什么前者计算结果为True吗?

并且,是否有一种可靠的方法来检查变量的类型是否等于来自typing模块的类型?


3
首先,type(x) is aisinstance(x, a)绝对不是相同的东西。一个对象可以是许多类型(带有继承层次结构),但type(x)只给出其最具体的单一类型。 - BrenBarn
是的,我在进行了一些测试后已经意识到了这一点。尽管如此,我仍然觉得将其包含在我的问题中,因为它通常被视为在Python中询问任何类型比较时的首选。 - McMuffinton
type(['foo', 'bar']) == List[str] 的求值结果是什么? - Chad S.
1
正如预期的那样,它的求值结果为False,因为type(['foo','bar'])仅求值为list。 - McMuffinton
1个回答

8

isinstance不进行真正的PEP 484类型检查。 文档中简单地提到:

通常不应使用 isinstance()issubclass() 检查类型。

typing模块以及它基于的collections.abcabc模块,使用了大量的__instancecheck____subclasscheck__魔法来使isinstanceissubclass表现得合理。但是它们并没有足够的支持来支持您的情况。也不是它们的目标支持它。

有没有一种可靠的方法来检查变量的类型是否等于来自typing模块的类型?

你不是在寻找类型相等。正如你自己指出的那样,[1, 2, 3]的类型是list,它不等于List[str]List[int]。你正在寻找类型检查,这更加复杂。

考虑以下内容:

def my_function():
    # ... 1000 lines of very complicated code ...

print(isinstance(my_function, Callable[[], int]))

您会期望这个程序会打印什么?您不能期望isinstance在运行时深入到my_function并推断它总是返回int。在Python中,这是不可行的。您需要一个具有访问my_function结构的“编译”时间类型检查器,或者明确的类型注释,或者——最可能的是两者都需要。


非常感谢您清晰简洁的回答。我完全理解为什么不能期望一个函数弄清楚另一个函数是否总是返回某种类型的变量,但我想象中应该有一种方法可以“检查”单个变量是否对应于来自typing模块的类型。虽然这可能需要手动检查列表中的每个项目是否属于某种类型,但在处理大型列表或嵌套类型(List [Dict [str,List [int]])的情况下,这将需要太长时间才能实现。再次感谢您的回答! - McMuffinton
1
@McMuffinton 我的观点是my_function和你的someList一样都是“单个变量”。如果你不希望在运行时推断my_function的类型,那么也没有理由期望someList可以推断其类型。 - Vasiliy Faronov
那么 isinstanceissubclass 到底应该做什么? - brandonscript
1
@remus 它们的主要目的是让你反思Python的动态类型,这基于一个简单的想法:每个对象都是一个类的实例,该类是零个或多个基类的子类。因此,someListlist的一个实例,listobject的一个子类。这些关系决定了方法解析,因此它们可以很容易地在运行时得到。然而,PEP 3119后来引入了ABC和一种覆盖isinstance/issubclass的方法,这就是让你感到困惑的原因。 - Vasiliy Faronov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接