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

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

1
2
3
4
5
6
import types

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

使用isinstance():

1
2
3
4
if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()

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

通常,在Python中,您当然希望您的代码支持继承(因为继承非常方便,所以阻止使用您的代码使用它将是非常糟糕的),所以isinstance比检查type的标识更糟糕,因为它无缝地支持继承。

注意,这并不是说isinstance是好的,它只是比检查类型的相等性好一些。正常,神谕的,首选的解决方案几乎总是"duck typing":尝试使用所需的参数的如果是某些类型,用try / except声明捕捉所有的异常,可能出现如果论点不是事实上的类型(或任何其他类型好duck-mimicking; -), except条款,尝试别的东西(使用的论点"似"是其他类型)。

但是,basestring是一种非常特殊的情况—一种仅允许您使用isinstance (strunicode子类basestring)的内置类型。字符串是序列(您可以循环它们,索引它们,切片它们,…),但是您通常希望将它们视为"标量"类型——处理所有类型的字符串(可能还有其他标量类型,例如。一种方法,所有容器(列表、集合、dicts,……)另一种方法,以及basestring + isinstance帮助您实现这一点——这个习语的总体结构是这样的:

1
2
3
4
5
6
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通常可以替代duck类型,但是通常没有很大的压力来做这件事(请参阅这里)。ABC和最近的Python版本中实现然而提供额外的好处:isinstance (issubclass)现在可以不仅仅意味着"一个实例的一个派生类"(特别是,任何类都可以与ABC"注册",这样它将显示作为一个子类,及其实例的实例ABC);而且abc还可以通过模板方法设计模式应用程序以一种非常自然的方式为实际子类提供额外的便利(有关TM DP的更多信息,请参阅这里和这里[[part II]])。

有关Python 2.6中提供的ABC支持的底层机制,请参见这里;对于他们的3.1版本,非常相似,请看这里。在这两个版本中,标准库模块集合(即3.1版本—非常相似的2.6版本,请参见这里)提供了几个有用的abc。

为了这个答案,关键要保持对abc (TM DP的更自然放置功能之外,相比经典Python UserDict.DictMixin等替代mixin类)是他们让isinstance (issubclass)更有吸引力和普遍的(在Python 2.6和未来)比以前(在2.5之前),因此,相比之下,在最近的Python版本中,使检查类型相等成为比以前更糟糕的实践。


下面是一个例子,isinstance实现了一些type无法做到的事情:

1
2
3
4
5
class Vehicle:
    pass

class Truck(Vehicle):
    pass

在本例中,卡车对象是一辆汽车,但您将得到以下信息:

1
2
3
4
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中对象的类型?


Differences between isinstance() and type() in Python?

类型检查和

1
isinstance(obj, Base)

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

1
isinstance(obj, (Base1, Base2))

而类型检查

1
type(obj) is Base

只支持引用的类型。

作为旁注,is可能比

1
type(obj) == Base

因为类是单例的。

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

在Python中,通常您希望允许参数的任何类型,并将其视为预期的类型,如果对象的行为不符合预期,则会引发适当的错误。这称为多态性,也称为duck-typing。

1
2
3
def function_of_duck(duck):
    duck.quack()
    duck.swim()

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

1
function_of_duck(mallard)

或者像鸭子一样工作:

1
function_of_duck(object_that_quacks_and_swims_like_a_duck)

我们的代码仍然有效。

然而,在某些情况下,显式类型检查是可取的。也许您对不同的对象类型有一些合理的处理。例如,panda Dataframe对象可以由dict或记录构造。在这种情况下,代码需要知道它得到的参数类型,以便能够正确地处理它。

所以,为了回答这个问题:

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

请允许我来说明其中的区别:

type

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

1
2
3
4
5
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替换的原则,那么这个子类可以被替换为类型),我们的代码就会中断!

1
2
3
from collections import OrderedDict

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

提出了一个错误!

1
2
3
4
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替换!

1
2
3
4
5
6
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协议,我们可以执行以下操作,并且我们的代码变得更加灵活:

1
2
3
4
5
6
from collections import Mapping

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

回应置评:

It should be noted that type can be used to check against multiple classes using type(obj) in (A, B, C)

是的,你可以测试类型是否相等,但是不要使用上面的方法,而是使用控制流的多个基,除非你特别只允许这些类型:

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

不同之处在于,isinstance支持子类,这些子类可以代替父类,而不会破坏程序,这是一个称为Liskov substitution的属性。

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

结论

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


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

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

或者,使用basestring抽象类:

1
2
if isinstance(b, basestring):
    do_something_else()

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

8.15. types — Names for built-in types

Starting in Python 2.2, built-in
factory functions such as int() and
str() are also names for the
corresponding types.

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


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

但是我们可以得到类似的abc。根据……instancecheck__。

以上abc.__instancecheck__,使用后测试如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 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:

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

isinstance:

1
2
# 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添加)