协慌网

登录 贡献 社区

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好的 ,请注意 - 它比检查类型的相等性更糟糕 。正常的,Pythonic,首选解决方案几乎总是 “鸭子打字”:尝试使用该参数,就好像它是某种期望的类型一样,在try / except语句中执行它,捕获可能出现的所有异常,如果参数实际上不是那种类型(或任何其他类型很好地模仿它;-),并在except子句中,尝试其他东西(使用参数 “好像” 它是其他类型)。

但是, basestring 一个非常特殊的情况 - 内置类型存在让你使用isinstancestrunicode子类basestring )。字符串是序列(你可以循环它们,索引它们,切片它们......),但你通常希望将它们视为 “标量” 类型 - 它有点不方便(但是一个相当频繁的用例)来处理各种类型的字符串(也许是其他标量类型,即你无法循环的那些)单向,所有容器(列表,集合,dicts,......)以另一种方式, basestring plus 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 的更多信息,请参见此处和 [ 此处 [[第 II 部分]],一般而言,特别是在 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 版本中比以前更糟糕。

这是一个例子,其中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 对象可以从 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 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)来检查多个类

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

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

另一个区别是, isinstance支持可以替代父类的子类,而不会破坏程序,这个属性称为 Liskov 替换。

但是,更好的是,反转你的依赖关系并且根本不检查特定的类型。

结论

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