type()和is实例()有什么区别?

这两个代码片段有什么区别?

使用#0

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

使用#0

if isinstance(a, dict):do_something()if isinstance(b, str) or isinstance(b, unicode):do_something_else()
835070 次浏览

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

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

或者,使用basestring抽象类:

if isinstance(b, basestring):do_something_else()

下面是一个例子,其中isinstance实现了type无法实现的东西:

class Vehicle:pass
class Truck(Vehicle):pass

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

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

换句话说,isinstance对于子类也是如此。

另见:如何比较Python中对象的类型?

根据python留档这里是一个声明:

8.15.类型-内置类型的名称

从Python 2.2开始,内置工厂函数,例如int()str()也是一个名字对应的类型。

所以#0应该优先于#1

总结一下其他(已经很好了!)答案的内容,isinstance迎合了继承(派生类是一个实例的基类实例,也是),而检查type不相等(它要求类型的标识并拒绝子类型的实例,AKA子类)。

通常情况下,在Python中,你希望你的代码支持继承,当然(因为继承是如此方便,阻止使用你的代码使用它是不好的!),所以isinstance比检查type的身份更糟糕,因为它无缝支持继承。

注意,isinstance并不是——它只是不那么糟糕,而不是检查类型的相等性。正常的Pythonic首选解决方案几乎总是“鸭子类型”:尝试使用参数好像它是某种所需的类型,在try/except语句中这样做,捕获所有可能出现的异常,如果参数实际上不是那种类型(或任何其他类型很好地模仿它;-),在except子句中,尝试其他东西(使用参数“好像”它是其他类型)。

然而,basestring是一个非常特殊的情况——一个存在只有的内置类型,允许您使用isinstancestrunicode子类basestring)。字符串是序列(您可以循环它们、索引它们、切片它们……),但您通常希望将它们视为“标量”类型——以一种方式处理各种字符串(可能还有其他标量类型,即您无法循环的类型),以另一种方式处理所有容器(列表、集合、字典……),basestringisinstance可以帮助您做到这一点——这个习语的整体结构类似于:

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通常可以替代鸭子打字,但通常没有太大的压力(见2)。然而,在最近的Python版本中实现的ABC确实提供了额外的好处:isinstance(和issubclass)现在可以不仅仅意味着“派生类的[实例]”(特别是,任何类都可以“注册”ABC,以便它将显示为子类,其实例作为ABC的实例); ABC还可以通过模板方法设计模式应用程序以非常自然的方式为实际子类提供额外的便利(参见这里这里[[Part II]]有关TM DP的更多信息,一般和特别是在Python中,独立于ABC)。

有关Python 2.6中提供的ABC支持的底层机制,请参阅这里;有关它们的3.1版本,非常相似,请参阅这里。在这两个版本中,标准库模块收藏(即3.1版本-有关非常相似的2.6版本,请参阅这里)提供了几个有用的ABC。

为了这个答案的目的,保留ABC的关键是(除了TM DP功能可以说是更自然的放置之外,与经典的Python替代混合类(如用户信息)相比),它们使isinstance(和issubclass)比以前(在Python 2.6和以后)更具吸引力和普遍性(在Python 2.5和之前),因此,相比之下,在最近的Python版本中,检查类型相等性的做法比以前更糟糕。

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

类型检查

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

所以,回答这个问题:

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 nowraise 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 fooValueError: 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可用于使用type(obj) in (A, B, C)检查多个类

是的,您可以测试类型是否相等,但不要使用上述方法,而是使用多个基进行控制流,除非您专门只允许这些类型:

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

同样,不同之处在于isinstance支持可以替换父类而不会破坏程序的子类,这种属性称为Liskov替换。

不过,更好的是,反转您的依赖项,根本不检查特定类型。

结论

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

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

然而,我们可以根据__instancecheck__得到类似的ABC__instancecheck__

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

# file tree# /test/__init__.py# /test/aaa/__init__.py# /test/aaa/aa.pyclass b():pass
# /test/aaa/a.pyimport syssys.path.append('/test')
from aaa.aa import bfrom 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__ NotImplementedTrueFalse

我得到这个结论,对于type

# according to `abc.__instancecheck__`, they are maybe different! I have not found negative onetype(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)})

顺便说一句:最好不要混合使用relative and absolutely import,使用project_dir的absolutely import(由sys.path添加)

一个实际的用法差异是它们如何处理booleans

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

isinstance(True, int)

isinstance(False, int)

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

type(True) == int

返回False

type()和is实例()的区别

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

isinstance()->返回一个布尔值

一般来说,isinstance是检查对象是否具有某种“类型”的“更”优雅的方式(因为您知道继承链)。

另一方面,如果您不了解继承链并且需要选择,请选择type(x) == ...

type的另一个有趣的例子是当你检查bool

----Case bool----
print(type(True) == int) # Falseprint(type(False) == int) # Falseprint(type(True) == bool) # Trueprint(type(False) == bool) # True
print(isinstance(True, int)) # Trueprint(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) # Trueprint(type(var2) == A) # Falseprint(type(var3) == A) # False
print(isinstance(var1, A)) # Trueprint(isinstance(var2, A)) # Trueprint(isinstance(var3, A)) # True


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


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