首页 文章

type()和isinstance()之间有什么区别?

提问于
浏览
1002

这两个代码片段之间有什么区别?使用 type()

import types

if type(a) is types.DictType:
    do_something()
if type(b) in types.StringTypes:
    do_something_else()

使用 isinstance()

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()

6 回答

  • 280

    这就是 isinstance 优于 type 的原因:

    class Vehicle:
        pass
    
    class Truck(Vehicle):
        pass
    

    在这种情况下,卡车对象是一辆车,但你会得到这个:

    isinstance(Vehicle(), Vehicle)  # returns True
    type(Vehicle()) == Vehicle      # returns True
    isinstance(Truck(), Vehicle)    # returns True
    type(Truck()) == Vehicle        # returns False, and this probably won't be what you want.
    

    换句话说, isinstance 也适用于子类 .

    另见:How to compare type of an object in Python?

  • 11

    对于真正的差异,我们可以在 code 中找到它,但是我找不到 isinstance() 的默认行为的实现 .

    但是我们可以根据instancecheck获得类似的abc.instancecheck .

    abc.__instancecheck__ 以上,使用下面的测试:

    # file tree
    # /test/__init__.py
    # /test/aaa/__init__.py
    # /test/aaa/aa.py
    class b():
    pass
    
    # /test/aaa/a.py
    import sys
    sys.path.append('/test')
    
    from aaa.aa import b
    from aa import b as c
    
    d = b()
    
    print(b, c, d.__class__)
    for i in [b, c, object]:
        print(i, '__subclasses__',  i.__subclasses__())
        print(i, '__mro__', i.__mro__)
        print(i, '__subclasshook__', i.__subclasshook__(d.__class__))
        print(i, '__subclasshook__', i.__subclasshook__(type(d)))
    print(isinstance(d, b))
    print(isinstance(d, c))
    
    <class 'aaa.aa.b'> <class 'aa.b'> <class 'aaa.aa.b'>
    <class 'aaa.aa.b'> __subclasses__ []
    <class 'aaa.aa.b'> __mro__ (<class 'aaa.aa.b'>, <class 'object'>)
    <class 'aaa.aa.b'> __subclasshook__ NotImplemented
    <class 'aaa.aa.b'> __subclasshook__ NotImplemented
    <class 'aa.b'> __subclasses__ []
    <class 'aa.b'> __mro__ (<class 'aa.b'>, <class 'object'>)
    <class 'aa.b'> __subclasshook__ NotImplemented
    <class 'aa.b'> __subclasshook__ NotImplemented
    <class 'object'> __subclasses__ [..., <class 'aaa.aa.b'>, <class 'aa.b'>]
    <class 'object'> __mro__ (<class 'object'>,)
    <class 'object'> __subclasshook__ NotImplemented
    <class 'object'> __subclasshook__ NotImplemented
    True
    False
    

    我得到了这个结论,对于 type

    # according to `abc.__instancecheck__`, they are maybe different! I have not found negative one 
    type(INSTANCE) ~= INSTANCE.__class__
    type(CLASS) ~= CLASS.__class__
    

    对于 isinstance

    # guess from `abc.__instancecheck__`
    return any(c in cls.__mro__ or c in cls.__subclasses__ or cls.__subclasshook__(c) for c in {INSTANCE.__class__, type(INSTANCE)})
    

    BTW:最好不要混用 relative and absolutely import ,使用project_dir中的 absolutely import (由 sys.path 添加)

  • 57

    根据python文档,这里有一个声明:

    8.15 . types - 内置类型的名称从Python 2.2开始,内置工厂函数(如int()和str())也是相应类型的名称 .

    所以isinstance()应优先于type() .

  • 1063

    总结其他(已经很好!)答案的内容, isinstance 迎合继承(派生类的实例也是基类的实例),而检查 type 的相等性则不然(它需要类型的身份和拒绝子类型的实例,AKA子类) .

    通常,在Python中,你希望你的代码支持继承,当然(因为继承非常方便,使用你的代码来阻止使用它的代码会很糟糕!),所以 isinstance 比检查 type 的标识要糟糕得多,因为它无缝地支持继承 .

    并不是说 isinstance 是好的,请注意 - 它比检查类型的相等性要差 . 正常的,Pythonic,首选解决方案几乎总是"duck typing":尝试使用该参数,就好像它是某种所需类型一样,在 try / except 语句中执行它,捕获可能出现的所有异常,如果参数实际上不属于该类型(或任何其他类型很好地模仿它;-),并在 except 子句中,尝试其他东西(使用参数"as if"它是其他类型的) .

    basestring is ,但是,非常特殊的情况 - 存在 only 的内置类型,让您使用 isinstancestrunicode 子类 basestring ) . 字符串是序列(你可以循环它们,索引它们,切片它们......),但你通常希望将它们视为"scalar"类型 - 它's somewhat incovenient (but a reasonably frequent use case) to treat all kinds of strings (and maybe other scalar types, i.e., ones you can't循环)单向,所有容器(列表,集合,dicts, ...)以另一种方式, basestring 加上 isinstance 帮助你做到这一点 - 这个成语的整体结构是这样的:

    if isinstance(x, basestring)
      return treatasscalar(x)
    try:
      return treatasiter(iter(x))
    except TypeError:
      return treatasscalar(x)
    

    你可以说 basestring 是一个抽象基类("ABC") - 它没有为子类提供具体的功能,而是作为"marker"存在,主要用于 isinstance . 这个概念在Python中显然是一个不断增长的概念,因为PEP 3119引入了它的概括,它被接受并且已经从Python 2.6和3.0开始实现 .

    PEP清楚地表明,虽然ABCs通常可以替代鸭子打字,但通常没有很大的压力(见here) . 然而,在最近的Python版本中实现的ABCs提供了额外的好处: isinstance (和 issubclass )现在不仅仅意味着"[an instance of] a derived class"(特别是,任何类都可以是带有ABC的"registered",因此它将显示为子类,其实例为ABC的实例);通过模板方法设计模式应用程序,ABCs还可以非常自然的方式为实际的子类提供额外的便利(有关TM DP的更多信息,请参阅herehere [[part II]],一般而言,特别是在Python中,独立于ABCs) .

    有关Python 2.6中提供的ABC支持的基础机制,请参阅here;对于他们的3.1版本,非常相似,请参阅here . 在这两个版本中,标准库模块collections(这是3.1版本 - 非常相似的2.6版本,请参阅here)提供了几个有用的ABC .

    出于这个答案的目的,保留关于ABCs的关键(除了可以说更加自然的TM DP功能放置,与混合类的经典Python替代方案,如UserDict.DictMixin相比),它们更多地使 isinstance (和 issubclass )更多有吸引力和普遍的(在Python 2.6和前进中)比以前(在2.5和之前),因此,相比之下,使检查类型相等在最近的Python版本中比以前更糟糕的做法 .

  • 67

    Python中的isinstance()和type()之间的区别?

    使用类型检查

    isinstance(obj, Base)
    

    允许子类的实例和多个可能的基础:

    isinstance(obj, (Base1, Base2))
    

    而使用 . 进行类型检查

    type(obj) is Base
    

    仅支持引用的类型 .


    作为旁注, is 可能比更合适

    type(obj) == Base
    

    因为 class 是单身人士 .

    避免类型检查 - 使用多态性(鸭子打字)

    在Python中,通常您希望允许任何类型的参数,按预期处理它,如果对象没有按预期运行,它将引发适当的错误 . 这称为多态,也称为鸭子打字 .

    def function_of_duck(duck):
        duck.quack()
        duck.swim()
    

    如果上面的代码有效,我们可以假设我们的参数是一个鸭子 . 因此我们可以传入其他东西是鸭子的实际子类型:

    function_of_duck(mallard)
    

    或者像鸭子一样工作:

    function_of_duck(object_that_quacks_and_swims_like_a_duck)
    

    我们的代码仍然有效 .

    但是,在某些情况下需要明确地进行类型检查 . 也许你对不同的对象类型有明智的关系 . 例如,Pandas Dataframe对象可以从dicts或记录构造 . 在这种情况下,您的代码需要知道它所获得的参数类型,以便它可以正确处理它 .

    那么,回答这个问题:

    Python中的isinstance()和type()之间的区别?

    请允许我展示一下差异:

    类型

    假设您的函数获得某种参数(构造函数的常见用例),则需要确保某种行为 . 如果你检查这样的类型:

    def foo(data):
        '''accepts a dict to construct something, string support in future'''
        if type(data) is not dict:
            # we're only going to test for dicts for now
            raise ValueError('only dicts are supported for now')
    

    如果我们尝试传入一个 dict 的子类的dict(我们应该可以,如果我们期望我们的代码遵循Liskov Substitution的原则,那个子类型可以代替类型)我们的代码会破坏!:

    from collections import OrderedDict
    
    foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))
    

    提出错误!

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in foo
    ValueError: argument must be a dict
    

    isinstance

    但是如果我们使用 isinstance ,我们可以支持Liskov Substitution!:

    def foo(a_dict):
        if not isinstance(a_dict, dict):
            raise ValueError('argument must be a dict')
        return a_dict
    
    foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))
    

    返回 OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

    抽象基类

    事实上,我们可以做得更好 . collections 提供了为各种类型强制执行最小协议的抽象基类 . 在我们的例子中,如果我们只期望 Mapping 协议,我们可以执行以下操作,并且我们的代码变得更加灵活:

    from collections import Mapping
    
    def foo(a_dict):
        if not isinstance(a_dict, Mapping):
            raise ValueError('argument must be a dict')
        return a_dict
    

    回复评论:

    应该注意,类型可以用于在(A,B,C)中使用类型(obj)检查多个类

    是的,你可以测试类型的相等性,但是除了上面的内容之外,使用多个基数来控制流,除非你特别只允许这些类型:

    isinstance(obj, (A, B, C))
    

    不同的是, isinstance 支持可替代父类的子类,而不会破坏程序,这是一种称为Liskov替换的属性 .

    但是,更好的是,反转您的依赖项,而不是检查特定类型 .

    结论

    因此,由于我们想要支持替换子类,在大多数情况下,我们希望避免使用 type 进行类型检查,而更喜欢使用 isinstance 进行类型检查 - 除非您确实需要知道实例的精确类 .

  • 0

    后者是首选,因为它将正确处理子类 . 事实上,你的例子可以更容易编写,因为 isinstance() 的第二个参数可能是一个元组:

    if isinstance(b, (str, unicode)):
        do_something_else()
    

    或者,使用 basestring 抽象类:

    if isinstance(b, basestring):
        do_something_else()
    

相关问题