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

1548

这两段代码片段有何不同之处?

使用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()

5
注意:如果不是strunicode(在这种情况下您可以使用basestring进行检查),则可以使用元组来检查多个类型。要检查something是否为intstr,请使用isinstance(something,(int,str)) - xuiqzy
2
type() 返回您作为参数输入的对象的类型,通常不太有用,除非与实际类型(例如 type(9) == int)进行比较。 isinstance() 根据对象是否属于给定类型返回布尔值 - true 或 false。 对于大多数情况,使用 isinstance 通常比编写混乱的等式检查更加优雅。 - user11991978
8个回答

1517
为了总结其他(已经很好的)答案的内容,isinstance适用于继承(派生类的实例也是基类的实例),而检查type的相等性则不适用(它要求类型的身份验证并拒绝子类型的实例,也就是子类)。 通常,在Python中,你当然希望你的代码支持继承(因为继承非常方便,如果阻止使用你的代码的代码使用它,那将是不好的!),所以isinstance比检查type的身份验证要好,因为它无缝地支持继承。 并不是说isinstance很好,注意,它只是比检查类型的相等性要稍微好一点。正常的、Pythonic的、首选的解决方案几乎总是“鸭子类型”:尝试使用参数作为某种期望的类型,使用try/except语句捕获可能出现的所有异常,如果参数实际上不是该类型(或任何其他漂亮的鸭子模仿它的类型),在except子句中尝试其他方法(使用参数“as if”是某种其他类型)。

basestring是一个特殊的内置类型,只存在于让你使用isinstancestrunicode都是basestring的子类)。字符串是序列(你可以循环、索引、切片等),但通常你希望将它们视为“标量”类型,这样做有点不方便(但是这是一个相当频繁的用例),将所有类型的字符串(也许还有其他标量类型,即你无法循环的类型)一种方式,所有容器(列表、集合、字典等)另一种方式,而basestring加上isinstance可以帮助你做到这一点。这个惯用语的总体结构大致如下:

if isinstance(x, basestring)
  return treatasscalar(x)
try:
  return treatasiter(iter(x))
except TypeError:
  return treatasscalar(x)
您可以将basestring视为一个抽象基类(“ABC”)——它不向子类提供任何具体的功能,而是作为一个“标记”,主要用于isinstance的使用。显然,这个概念在Python中正在不断发展,自PEP 3119以来,它已经被接受,并从Python 2.6和3.0开始实现。 PEP明确指出,虽然ABC通常可以替代鸭式编程,但通常没有太大的压力去做到这一点(请参见here)。最近Python版本中实现的ABC确实提供了额外的好处:isinstance(和issubclass)现在可以意味着更多,而不仅仅是“[派生类的]一个实例”(特别是,任何类都可以与ABC“注册”,以便它将显示为子类,其实例为ABC的实例);而且ABC还可以通过模板方法设计模式应用的非常自然的方式为实际子类提供额外的便利(有关TM DP的更多信息,请参见herehere [[part II]],独立于ABC,在一般和特定于Python的情况下)。

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

为了回答本问题,关于ABC需要记住的关键事情(除了与经典Python替代方案(例如UserDict.DictMixin混合类)相比更自然地放置TM DP功能之外),就是它们使得isinstance(和issubclass)在Python 2.6及以后的版本中比以前更具吸引力和普遍性(在2.5及以前的版本中),因此,相比之下,在最近的Python版本中检查类型等式的做法比以前更糟糕。


33
并不是说isinstance就很好,要注意的是——它只是比检查类型相等更少糟糕一点。通常、符合Python风格的首选解决方案几乎总是“鸭子类型”。这种观点有些片面:例如在解释器中,类型反映了语法规则,使用isinstance()非常合适。追求“Pythonic”并不是万能的! - Gene Callahan
3
在Python 3中,basestring已不再可用。 - erobertc
4
当没有客观标准时,我从来不理解形容词“better”和“worse”。如果不知道为什么而言,事物怎么可能是“better”的呢?如果你不想检查子类的话,“isinstance”函数并不“better”,它只是做了一件“不同”的事情。 - Eduardo Pignatelli
2
@EduardoPignatelli:对于这样的主观用途,“更好”实际上只意味着“当您没有强烈理由偏好特定方法时,应该选择哪个作为默认值?”在这里,如果您没有已知的要求来排除子类,则默认使用isinstance,即使您不知道任何子类需要处理,因为如果第三方使用您的代码想要传递自己的类型子类,它更有可能起作用;如果子类违反了预期,它可能不起作用,但是如果您拒绝完全处理子类,则肯定不会起作用。 - ShadowRanger
1
类似地,如果您没有必须严格进行类型检查的情况,则鸭子类型比isinstance检查更好。有些情况确实需要进行类型检查。避免使用鸭子类型的常见情况是,当您需要执行步骤X和Y时,其中Y依赖于X的完成。 X分配了一些昂贵的资源或执行了一些难以撤消的操作,但是只有在满足鸭子类型时,才会失败Y。在执行X之前,您需要验证输入是否符合Y要求,而isinstance检查可能是唯一合理的方法,即使这意味着拒绝可能有效的输入。 - ShadowRanger
显示剩余3条评论

440
下面是一个示例,其中isinstance实现了type无法实现的功能:
class Vehicle:
    pass

class Truck(Vehicle):
    pass
在这种情况下,一个Truck对象是一个Vehicle,但你会得到这个:
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()对于子类也是成立的。 另请参见:如何在Python中比较对象的类型?

172
因为存在一些情况你不希望使用isInstance的行为,所以我认为没有所谓的“更好”。它们只是做了不同的事情。 - philgo20
33
“isinstance比type更好”这一评论是误导性的。乍一看,它被理解为“type已经过时,应该使用isinstance”。例如,我需要进行type()检查,但由于这个原因被误导了一小段时间(并且需要进行一些调试)。请注意,不要改变原意,并尽量使语言通俗易懂。 - ceremcem
9
这是它们工作方式不同的一个很好的例子,但我遇到了一个情况,我特别需要 type() 而不是 isinstance()。两者并没有优劣之分;它们适用于不同的事情。 - EL_DON
2
请问你为什么使用 "==" 而不是使用 "is"? - variable
1
@variable 如果两个变量指向同一个对象,则返回True,如果变量所引用的对象相等,则返回==。请参阅此SO帖子。我不确定在这个答案的上下文中是否重要。 - LSgeo

130

Python中isinstance()type()的区别是什么?

type()用于类型检查,而isinstance()用于对象实例检查。

isinstance(obj, Base)

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

isinstance(obj, (Base1, Base2))

与之相反的是使用类型检查

type(obj) is Base

仅支持所引用的类型。


顺便提一下,is 可能比

更加合适。
type(obj) == Base

因为类是单例的。

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

在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对象可以从字典或记录构建。在这种情况下,您的代码需要知道它获得的参数类型,以便可以正确处理它。

因此,回答这个问题:

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

请允许我演示一下区别:

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 的子类(如果我们期望我们的代码遵循 Liskov替换原则,即子类型可以替换类型),我们的代码就会失败!:

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替换原则!

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

回应评论:

需要注意的是,可以使用type(obj) in (A, B, C)来检查多个类的类型。

是的,您可以测试类型的相等性,但除非您仅允许那些类型,否则请使用多个基础控制流。

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

区别在于 isinstance 支持子类作为父类的替代品而不会破坏程序的其他部分,这个属性被称为 Liskov 替换原则。

更好的方法是,反转你的依赖关系,根本不检查特定类型。

结论

因此,由于我们希望支持替代子类,大多数情况下,我们要避免使用 type 进行类型检查,并优先考虑使用 isinstance 进行类型检查——除非您确实需要知道实例的精确类。


如果你有一个名为your_module.py的模块,在其中检查isinstance(instance, y)并使用from v.w.x import y,并且你导入了该检查,但是当你实例化instance时,你使用的是from x import y而不是在your_module.py中导入y的方式,那么即使它是相同的类,isinstance检查也会失败。 - toonarmycaptain

70

更推荐使用后者,因为它可以正确处理子类。实际上,你的例子还可以更加简单,因为isinstance()函数的第二个参数可以是一个元组:

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

或者,使用basestring抽象类:

if isinstance(b, basestring):
    do_something_else()

22

一个实用的区别是它们如何处理布尔值 (booleans) :

TrueFalse 只是在 Python 中表示 10 的关键字。因此,

isinstance(True, int)

并且

isinstance(False, int)

两者都返回True。两个布尔值都是整数的实例。然而,type()函数更聪明:

type(True) == int

返回值为False


15

1

要了解真正的区别,我们可以在代码中找到它,但我找不到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)})

顺便说一句:最好不要混合使用相对和绝对导入,使用从项目目录中添加到sys.path绝对导入


1

type() 和 isinstance() 的区别

type() -> 返回对象的类型

isinstance() -> 返回布尔值

一般而言,isinstance 是更为优雅的检查对象是否为某种“类型”的方式(因为你了解继承链)。

但是,如果你不了解继承链并需要进行选择,则使用 type(x) == ...

另一个有趣的 type 情况是当你检查 bool 时。

----Case bool----

print(type(True) == int) # False
print(type(False) == int) # False
print(type(True) == bool) # True
print(type(False) == bool) # True

print(isinstance(True, int)) # True
print(isinstance(True, int)) # True



----Case inheritance----
class A:
    x=1

class B(A):
    x=2

class C(B):
    x=3
    
var1 = A()
var2 = B()
var3 = C()

print(type(var1)) # <class '__main__.A'>
print(type(var1) == A) # True
print(type(var2) == A) # False
print(type(var3) == A) # False

print(isinstance(var1, A)) # True
print(isinstance(var2, A)) # True
print(isinstance(var3, A)) # True



print(type(var2)) # <class '__main__.B'>
print(type(var1) == B) # False
print(type(var2) == B) # True
print(type(var3) == B) # False

print(isinstance(var1, B)) # False
print(isinstance(var2, B)) # True
print(isinstance(var3, B)) # True



print(type(var3)) # <class '__main__.C'>
print(type(var1) == C) # False
print(type(var2) == C) # False
print(type(var3) == C) # True

print(isinstance(var1, C)) # False
print(isinstance(var2, C)) # False
print(isinstance(var3, C)) # True

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