【问题标题】:isinstance(foo,bar) vs type(foo) is barisinstance(foo,bar) vs type(foo) is bar
【发布时间】:2014-03-20 14:45:40
【问题描述】:

一个语义问题,真的。

直到最近,如果我必须对结构进行任何类型检查,我会使用 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 is 的COMPARE_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): 更易读,第一个基本上只是伪代码,第二个调用一些函数(我每次都必须查找 isinstance 或 @987654333 @) 有一些论据。它看起来不像是类型转换,并且没有明确的方法可以知道 isinstance(a,b) 是否正在检查 b 是否是 a 的实例,反之亦然。

我从this question 了解到,我们使用isinstance 是因为它对继承更好。 type(ClassDerivedFromList) is list 将失败,而 isinstance(ClassDerivedFromList,list) 将成功。但是,如果我要检查什么应该始终是基本对象,那么我在做type is 时真正失去了什么?

【问题讨论】:

    标签: python typechecking


    【解决方案1】:

    如果我正在检查什么应该始终是基本对象,那么我在做 type is 时真正失去了什么?

    嗯,很高兴您在问题中给出完整的文档答案,所以您的答案是您什么都没有!唯一需要isinstance() 的时候是在检查给定类与另一个类的继承时,正如您所说和引用的那样。 type() 仅用于检查实例是否完全属于给定的基本类型。

    【讨论】:

    • 我正在特别考虑重构我大约 6 个月前编写的一个函数,该函数接受字符串或字符串列表。该函数的唯一输入永远是str 或list,那么if type(foo) is str: _handle_str(foo) else: _handle_list(foo) 的做法真的很糟糕吗?
    • @adsmith:你使用 Python 3 吗?如果没有,如果你以一个 unicode 字符串结束,你的代码就会崩溃。
    • @DSM 我愿意,但我确实很欣赏 unicode 与 str 的缺陷。
    • 或者你可以简单地做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")。
    • 如果您对其他序列类型(尤其是 tuple 和 set)的容忍度更高,那么您的 lib 将更容易被其他人采用,或者迁移到新功能。接受生成器表达式代替您的列表也是具有前瞻性的。
    【解决方案2】:

    除了继承问题,您还无法在使用isinstance 时测试多种类型。例如:

    def chk(typ):
        if not isinstance(typ, (str, int)):
            raise ValueError('typ must be string or int')
        ...
    

    【讨论】:

    • 你总是可以做到type(x) in (str, int)。
    【解决方案3】:

    您的问题的答案是:
    不,因为您正在检查确定的基类,因为您失去了测试继承类的能力。

    IMO isinstance 在 Python 中更易于阅读:可读性很重要。

    PS:我在时间上发现了显着差异(在 Python 3.3 上)

          type: 0.5241982917936874  
    isinstance: 0.46066255811928847
    

    【讨论】:

    • 您发现isinstance(foo,list) 比type(foo) is list 更容易阅读??
    【解决方案4】:

    Python Docs for the built-in type function 提供了关于类型(带有一个 arg)和 isinstance 之间区别的明确指导。

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

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

    那就看看下面基于模块 diamond 的 python 控制台输出:

    根据文档,它更通用,可以涵盖更广泛的场景。关于 OP 的原始问题 - 性能特征并未作为偏爱另一个的正当理由。可读性也不行。

    为了更深入的讨论,其中包括解释(用回答者的话)“为什么在最近的 Python 版本中检查类型相等性比以前更糟糕” em>,请看这个高票answer

    【讨论】:

    • 我很欣赏这个答案,但你在这里谈论的问题的唯一部分是我在我的问题中回答的部分。 “我明白......我们使用isinstance,因为它更好地继承。type(ClassDerivedFromList) is list 将失败,而isinstance(ClassDerivedFromList, list) 将成功”
    • @AdamSmith - 正如我在回答中指出的那样,文档清楚地解释了差异。 " 推荐使用 isinstance() 内置函数来测试对象的类型,因为它考虑了子类。我正在澄清你的不确定性——以及为什么你注意到它是压倒性的流行选择。它更通用,可以涵盖更广泛的场景 - type() 在您知道确切类型的情况下就足够了。还希望用一些代码示例来说明这个答案,以便为其他人澄清。
    • 如果您投了反对票,如果您能提供解释,我将不胜感激,以便我在认为有必要时修改/改进答案。谢谢!
    • 我没有投反对票,但这个答案专门针对问题明确没有询问的部分。对我来说似乎很奇怪。该问题专门询问了人们想要测试确切类型时的情况,而答案谈到了isinstance 在不想测试确切类型时更好。
    • 我的意思是,感谢您的有用评论 - isInstance 更通用,因此很受欢迎。没有将性能特征作为偏爱其中一个的理由。因此,除非他们“需要”不这样做,否则大多数人都会选择更通用的选项。
    【解决方案5】:

    type(x) 不适用于 mypy

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

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-25
      • 2017-06-23
      • 2012-01-25
      相关资源
      最近更新 更多