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

935
2017-10-11 03:50


起源




答案:


总结其他(已经很好!)答案的内容, isinstance 迎合继承(派生类的一个实例) 是一个 同时检查是否存在基类的实例 type 不(它要求类型的身份并拒绝子类型的实例,AKA子类)。

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

这并不是说 isinstance 是 ,记住你 - 它只是 不那么糟糕 而不是检查类型的相等性。正常的,Pythonic,首选解决方案几乎总是“鸭子打字”:尝试使用这个论点 仿佛 它是一种特定的类型,在一个 try/except 声明捕获所有异常,如果参数实际上不是那种类型(或任何其他类型很好地模仿它;-),并在 except 子句,尝试别的东西(使用参数“好像”它是某种其他类型)。

basestring  然而,这是一个非常特殊的案例 - 存在的内置类型 只要 让你用 isinstance (都 str 和 unicode 子类 basestring)。字符串是序列(你可以循环它们,索引它们,切片它们......),但是你通常希望将它们视为“标量”类型 - 它有点不方便(但是一个相当频繁的用例)来处理各种类型的字符串(也许是其他标量类型,即你不能循环的那些)单向,所有容器(列表,集合,dicts,......)以另一种方式,以及 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清楚地表明,虽然ABCs通常可以替代鸭子打字,但通常没有很大的压力(见 这里)。然而,在最近的Python版本中实现的ABCs提供了额外的好处: isinstance (和 issubclass)现在不仅仅意味着“[派生类的一个实例]”(特别是,任何类都可以用ABC“注册”,以便它显示为子类,并将其实例显示为ABC的实例);和ABC也可以通过模板方法设计模式应用程序以非常自然的方式为实际的子类提供额外的便利(参见 这里 和 这里 [[第二部分]]了解有关TM DP的更多信息,一般而言,特别是在Python中,与ABCs无关)。

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

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


991
2017-10-11 04:31



你应该提到允许覆盖的抽象基类(ABCs) issubclass 和 isinstance。见[见abc] [1]。 [1]: docs.python.org/library/abc.html - Andrey Vlasovskikh


这就是原因 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中对象的类型?


260
2017-10-11 03:58



因为在某种情况下你不想要isInstance行为,我认为没有“更好”。他们只是做了不同的事情。 - philgo20
-1,因为“isinstance比type更好”是一个误导性的评论。它被理解为“type 已弃用,请使用 isinstance 相反,“乍一看。例如,我想要的是完全正确的 type() 检查,但由于这个原因我被误导了很短的时间(并且不得不调试一下)。 - ceremcem
这是他们如何以不同方式工作的一个很好的例子,但我遇到了一个我特别需要的案例 type() 并不是 isinstance()。一个不是更好;他们是为了不同的事情。 - EL_DON
-1,将“isinstance比type”更改为“isinstance可能更有用,如果您希望以下工作类型”。如果您不想包含父类,有很多情况下type会比isinstance更好。 - David Callanan


之间的差异 isinstance() 和 type() 在Python?

使用类型检查

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

那么,回答这个问题:

之间的差异 isinstance() 和 type() 在Python?

请允许我展示一下差异:

type

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

def foo(data):
    '''accepts a dict to construct something, string support in future'''
    if type(data) is dict:
        # we're only going to test for dicts for now
        raise ValueError('only dicts are supported for now')

如果我们尝试传入一个dict,它是一个子类 dict (正如我们应该能够的,如果我们期望我们的代码遵循原则 利斯科夫换人,那个子类型可以代替类型)我们的代码中断了!:

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)

是的,您可以测试类型是否相等,但是除了上述内容之外,使用多个基数来控制流,除非您特别只允许这些类型:

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

不同的是,差异在于 isinstance 支持可以替换父类的子类,而不会破坏程序,这个属性称为Liskov替换。

但是,更好的是,反转您的依赖项,而不是检查特定类型。

结论

因此,既然我们想支持替换子类,在大多数情况下,我们希望避免使用类型检查 type 并且更喜欢用类型检查 isinstance  - 除非你真的需要知道一个实例的精确类。


61
2018-02-06 04:13





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

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

或者,使用 basestring 抽象类:

if isinstance(b, basestring):
    do_something_else()

55
2017-10-11 03:54





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

8.15。 types - 内置类型的名称

从Python 2.2开始,内置   工厂的功能如 int() 和    str() 也是的名字   相应的类型。

所以 isinstance() 应该优先考虑 type()


11
2017-10-11 03:59





对于真正的差异,我们可以找到它 code,但我找不到默认行为的实现了 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

我得到这个结论, 对于 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 来自project_dir(由...添加) sys.path


0
2018-05-10 08:00