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

这两个代码片段之间有什么区别?使用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()

答案

总结其他答案(已经很好!)的内容时, isinstance可以满足继承(派生类实例也是基类实例),而检查type相等性则不需要(它要求类型和类型的标识)。拒绝子类型(AKA 子类)的实例。

通常,在 Python 中,当然,您希望您的代码支持继承(由于继承非常方便,因此停止使用您的代码来使用它会很糟糕!),所以isinstance比检查type s 的身份要糟糕得多,因为无缝支持继承。

这并不是说isinstance ,你要知道,它只是比检查的类型平等那么糟糕 。正常的,Python 式的首选解决方案几乎总是 “鸭式输入”: 使用某个特定类型一样尝试使用该参数,在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 。自从引入PEP 3119 (引入了它的泛化)以来,该概念显然在 Python 中正在不断发展。自从PEP 3119以来,该概念已从 Python 2.6 和 3.0 开始实施。

PEP 明确指出,尽管 ABC 常常可以代替鸭类打字,但这样做通常没有太大的压力(请参阅此处 )。但是,在最新的 Python 版本中实现的 ABC 确实提供了额外的好处: isinstance (和issubclass )现在的含义不仅仅是 “派生类的实例”(尤其是,任何类都可以在 ABC 中 “注册”),以便它会显示为子类,其实例显示为 ABC 的实例);并且 ABC 还可以通过模板方法设计模式应用程序以一种非常自然的方式为实际子类提供额外的便利(有关 TM DP 的更多信息,请参见此处此处的 [[Part II]],有关 Python 的更多信息,特别是在 Python 中,与 ABC 无关) 。

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

出于这个答案的目的,保留 ABC 的关键是(与 TM DP 混合类的经典 Python 替代类(例如UserDict.DictMixin相比,TM DP 功能可以说是更为自然的放置))是它们使isinstance (和issubclass )成为可能。 (在 Python 2.6 及更高版本中)比以前(在 2.5 及更低版本中)更具吸引力和普遍性,因此,相比之下,使类型相等性检查在最近的 Python 版本中比以前更加糟糕。

这是一个示例,其中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对于子类也适用。

另请参阅: 如何在 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 对象可以由字典记录构造。在这种情况下,您的代码需要知道它获取的参数类型,以便它可以正确处理它。

所以,要回答这个问题:

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

对评论的回应:

应该注意的是, type(obj) in (A, B, C)使用type(obj) in (A, B, C)使用type(obj) in (A, B, C)来检查多个类。

是的,您可以测试类型的相等性,但是除了上面的以外,请使用多个基础进行控制流,除非您专门允许这些类型:

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

再次不同的是, isinstance支持可替换父类的子类,而无需破坏程序,这是一种称为 Liskov 替换的属性。

更好的是,倒置依赖项,根本不检查特定类型。

结论

因此,既然我们要支持代子类,在大多数情况下,我们要避免进行类型检查与type和喜欢的类型检查与isinstance - 除非你真的需要知道确切类的实例。

首选后者,因为它将正确处理子类。实际上,由于isinstance()的第二个参数可能是元组,因此您的示例编写起来甚至更加容易:

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

或者,使用basestring抽象类:

if isinstance(b, basestring):
    do_something_else()

根据 python 文档,这是一条语句:

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

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

因此, isinstance()应该比type()更可取。

实际用法上的区别是它们如何处理booleans

TrueFalse只是在 python 中表示10关键字。从而,

isinstance(True, int)

isinstance(False, int)

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

type(True) == int

返回False

对于真正的差异,我们可以在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)})

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