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

使用类型:

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

当前回答

实际用法的不同之处在于它们如何处理布尔值:

True和False只是python中表示1和0的关键字。因此

isinstance(True, int)

and

isinstance(False, int)

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

type(True) == int

返回False。

其他回答

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

然而,我们可以得到类似的abc__instancecheck__根据__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

我得到这个结论,对于类型:

# 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:最好不要混用相对导入和绝对导入,使用project_dir中的绝对导入(由sys.path添加)

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

类型检查方式

isinstance(obj, Base)

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

isinstance(obj, (Base1, Base2))

而类型检查

type(obj) is Base

仅支持引用的类型。


作为旁注,这可能比

type(obj) == Base

因为类是单独的。

避免类型检查-使用多态性(duck类型)

在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,它是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,我们可以支持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')]))

return OrderedDict([('fo','bar'),('fizz','buzz')])

抽象基类

事实上,我们可以做得更好。集合提供抽象基类,这些抽象基类为各种类型强制执行最小协议。在我们的情况下,如果我们只期望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替换。

但更好的是,颠倒依赖关系,根本不检查特定类型。

结论

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

为了总结其他(已经很好了!)答案的内容,isinstance迎合了继承(派生类的实例也是基类的实例),而检查类型的相等性则没有(它要求类型的身份,拒绝子类型(AKA子类)的实例)。

通常,在Python中,您希望您的代码支持继承(因为继承非常方便,所以阻止使用您的代码使用它是不好的!),所以与检查类型标识相比,isinstance没有那么糟糕,因为它无缝地支持继承。

这并不是说这是好的,但请注意,这比检查类型的相等性差得多。正常的、Python式的、首选的解决方案几乎都是“duck-type”:尝试将参数用作某个所需的类型,在try/except语句中执行,捕获如果参数实际上不是该类型(或任何其他类型很好地模仿它;-)可能出现的所有异常,在except子句中,尝试其他方法(使用参数“好像”它是其他类型的)。

然而,basestring是一种非常特殊的内置类型,它只允许您使用isinstance(str和unicode子类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开始实现。

政治公众人物明确表示,虽然ABC通常可以代替鸭子打字,但一般来说,这样做没有太大的压力(见这里)。然而,在最近的Python版本中实现的ABC确实提供了额外的好处:isinstance(和issubclass)现在不仅仅意味着“派生类的实例”(特别是,任何类都可以向ABC“注册”,这样它将显示为子类,其实例将显示为ABC的实例);而ABC也可以通过模板方法设计模式应用程序以非常自然的方式为实际的子类提供额外的便利(参见此处和此处的[[第二部分]],了解有关TM DP的更多信息,通常是在Python中,独立于ABC)。

有关Python 2.6中提供的ABC支持的基本机制,请参阅此处;对于他们的3.1版本,非常相似,请参见此处。在这两个版本中,标准库模块集合(这是3.1版本,与2.6版本非常相似,请参见此处)提供了几个有用的ABC。

为了回答这个问题,要保留ABC的关键之处(除了与UserDict.DictMixin之类的混合类的经典Python替代品相比,TM DP功能的放置更自然之外)是,它们使isinstance(和issubclass)比过去(在Python 2.6和之前)更具吸引力和普遍性,因此,相比之下,在最近的Python版本中,检查类型相等比以前更糟糕。

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

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

因此,isinstance()应该优先于type()。

下面是一个例子,isinstance实现了类型无法实现的功能:

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.

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

另请参阅:如何比较Python中对象的类型?