isinstance(foo, bar)与type(foo) is bar的区别

31

其实这只是一个语义问题。

直到最近,如果我必须对一个结构进行任何类型检查,我会使用type(obj) is list等。但自从加入SO以来,我注意到每个人(我是说每个人)都使用isinstance(obj,list)。它们似乎是同义词,并且timeit显示它们之间的速度几乎完全相同。

def a(): return type(list()) is list
def b(): return isinstance(list(),list)

from timeit import timeit
timeit(a)
# 0.5239454597495582
timeit(b)
# 0.5021292075273176

实际上,即使是dis也认为它们是同义词,除了type isCOMPARE_OP之外。

from dis import dis

dis(a)
# 2           0 LOAD_GLOBAL              0 (type) 
#             3 LOAD_GLOBAL              1 (list) 
#             6 CALL_FUNCTION            0 (0 positional, 0 keyword pair) 
#             9 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
#            12 LOAD_GLOBAL              1 (list) 
#            15 COMPARE_OP               8 (is) 
#            18 RETURN_VALUE

dis(b)
# 2           0 LOAD_GLOBAL              0 (isinstance)
#             3 LOAD_GLOBAL              1 (list) 
#             6 CALL_FUNCTION            0 (0 positional, 0 keyword pair) 
#             9 LOAD_GLOBAL              1 (list) 
#            12 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
#            15 RETURN_VALUE 

我更喜欢使用if type(foo) is list:而不是if isinstance(foo,list):,前者基本上只是伪代码,而后者会调用一些函数(每次都要查找以确定是否为isinstanceinstanceof),并带有一些参数。它看起来不像类型转换,也没有明确的方式知道isinstance(a,b)是检查b是否是a的实例还是反之。

我从这个问题中了解到我们使用isinstance,因为它在继承方面更好。当type(ClassDerivedFromList) is list失败时,isinstance(ClassDerivedFromList,list)将成功。但如果我正在检查应始终是基础对象的内容,那么使用 type 会失去什么呢?

5个回答

21
如果我正在检查应始终作为基本对象的内容,那么使用 type is 会失去什么呢?
嗯,很好,你在问题中给出了完整的文档答案,所以你失去的是没有!只有在检查一个给定类相对于另一个类的继承关系时,isinstance() 才是必要的,正如你所说并引用的那样。 type() 应该仅用于检查实例是否确切地属于给定的基本类型。

我特别考虑重构我大约6个月前编写的一个函数,该函数接受字符串或字符串列表。该函数的唯一输入将永远是strlist,因此执行if type(foo) is str: _handle_str(foo) else: _handle_list(foo)是否真的是一种不好的做法? - Adam Smith
3
你使用Python 3吗?如果不是,如果你最终得到了一个Unicode字符串,那么你的代码就会出问题。 - DSM
@DSM 我确实了解unicodestr之间的陷阱,但我很欣赏它们。 - Adam Smith
3
或者你可以简单地使用 if type(foo) is list: _handle_list(foo) else: _handle_str(foo)。@DSM 是完全正确的,为了正确处理字符串,你需要检查 isinstance(foo, basestring)。如果我是你,我会实际检查 if type(foo) is list: _handle_list(foo) elif isinstance(foo, basestring): _handle_str(foo) else raise Exception("Type Not Handled") - zmo
1
如果你对除了列表之外的其他序列类型(尤其是元组和集合)更加宽容,那么你的库将更容易被其他人采用或迁移到新功能。接受生成器表达式来代替列表也是具有前瞻性的。 - PaulMcG
显示剩余2条评论

5
除了继承问题之外,当使用isinstance时,您还会失去测试多个类型的能力。例如:
def chk(typ):
    if not isinstance(typ, (str, int)):
        raise ValueError('typ must be string or int')
    ...

19
你可以始终使用 type(x) in (str, int)。该语句的意思是判断变量 x 的类型是否为字符串或整数,如果是则返回 True。请注意,不要改变原来的意思。 - pradyunsg

5

Python内置类型函数文档清晰地说明了type(带一个参数)和isinstance之间的区别。

使用一个参数返回对象的类型。返回值是一个类型对象,通常与object.class返回的对象相同。 建议使用内置函数isinstance()来测试对象的类型,因为它考虑了子类。

为了说明,让我们看一下diamond模块中的继承层次结构:

enter image description here

请看下面基于diamond模块的Python控制台输出:

enter image description here

根据文档,它更加通用,可以涵盖更广泛的场景。关于原帖提出的问题 - 性能特征没有被认为是支持一个而不是另一个的有效原因。可读性也不是。如需更深入的讨论,请参见这个高票答案,其中包括对(回答者的话)"为什么在最近的Python版本中检查类型相等性比以前更糟糕"的解释。

我很感谢你的回答,但是你在这里谈论的问题只是我在问题中回答的一部分。“我理解...我们使用isinstance是因为它对继承更友好。type(ClassDerivedFromList) is list会失败,而isinstance(ClassDerivedFromList, list)会成功”。 - Adam Smith
@AdamSmith - 正如我在我的回答中指出的那样,文档清楚地解释了区别。"isinstance()内置函数被推荐用于测试对象的类型,因为它考虑了子类。" 我澄清了你的疑虑 - 以及你注意到它是压倒性的流行选择的原因。它更加灵活,可以涵盖更广泛的场景 - 在您知道确切类型的情况下,type()就足够了。我还希望通过一些代码示例来说明这个答案,以便为其他人澄清。 - arcseldon
如果您给我点“踩”,我会非常感激如果您能提供一个解释,这样我就可以根据需要修改/改进答案。谢谢! - arcseldon
我没有点踩,但这个答案特别是关于问题明确没有问到的部分。对我来说似乎很奇怪。问题明确询问了当一个人想要测试一个确切类型的情况,而答案则谈到了当一个人不想测试一个确切类型时isinstance更好的方法。 - ShreevatsaR
我的意思正是如此,感谢您的有益评论 - isInstance更加灵活,因此更受欢迎。性能特征并没有被列为支持一种方法而不是另一种方法的原因。因此,除非他们“需要”做其他事情,否则大多数人会选择更加灵活的选项。 - arcseldon

1

type(x)在mypy中不起作用

至少对我而言,更喜欢使用isinstance而非type(x)的主要原因是因为mypy可以从isinstance检查中推断出类型,但无法从type(x)中推断。来自文档:

当使用isinstance类型测试时,Mypy通常可以正确地推断出类型,但对于其他类型的检查,您可能需要添加显式类型转换:

def f(o: object) -> None:
    if type(o) is int:
        o = cast(int, o)
        g(o + 1)    # This would be an error without the cast
        ...
    else:
        ...

来源:https://mypy.readthedocs.io/en/stable/common_issues.html?highlight=isinstance#complex-type-tests


0
你的问题的答案是:
不,因为你正在检查一个明确的基类,所以你失去了测试继承类的能力。
在我看来,isinstance 更易读,在 Python 中:可读性很重要。
PS:我在 Python 3.3 上得到了显著的时间差异。
      type: 0.5241982917936874  
isinstance: 0.46066255811928847

11
你认为isinstance(foo, list)type(foo) is list更易读? - Adam Smith

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