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

2024-04-20 14:33:01 发布

您现在位置:Python中文网/ 问答频道 /正文

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

Tags: 代码inimportifistypedoelse
3条回答

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

通常,在Python中,您希望您的代码支持继承,当然(因为继承非常方便,所以阻止使用您的代码的代码使用它是不好的!),因此isinstance比检查types的标识差得多,因为它无缝地支持继承。

这并不是说isinstance好的,请注意,它只是比检查类型的相等性好。正常的、Pythonic的、首选的解决方案几乎总是“duck typing”:尝试使用参数就好像它是某个所需的类型一样,在try/except语句中执行它,捕获如果参数不是该类型的事实(或任何其他类型的duck很好地模仿它;-)可能出现的所有异常,在except子句中,尝试其他操作(使用参数“好像”它是其他类型的)。

basestring的,然而,这是一个相当特殊的情况——一种内置类型,它只存在于中,允许您使用isinstance(同时存在于strunicode子类basestring)。字符串是序列(你可以循环它们,索引它们,切片它们,…),但是你通常想把它们当作“标量”类型,单向地处理所有类型的字符串(可能还有其他标量类型,即你不能循环的类型),所有容器(列表,集合,dict,…)另一方面,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开始实现。

政治公众人物清楚地表明,虽然ABCs通常可以替代duck类型,但通常没有很大的压力来做这件事(参见here)。然而,在最近的Python版本中实现的ABC确实提供了额外的好处:isinstance(和issubclass)现在不仅仅意味着“派生类的实例”(特别是,任何类都可以“注册”到ABC,这样它将显示为子类,并且其实例将显示为ABC的实例);ABC还可以为通过模板方法设计模式应用程序以非常自然的方式实际的子类(参见herehere[[第二部分]]了解有关TM DP的更多信息,通常和特别是在Python中,独立于abc)。

对于Python 2.6中提供的ABC支持的底层机制,请参见here;对于它们的3.1版本,非常类似,请参见here。在这两个版本中,标准库模块collections(这是非常相似的2.6版本的3.1版本,请参阅here)提供了几个有用的abc。

为了回答这个问题,关键是要保留关于ABCs的内容(除了TM DP功能更自然的位置之外,与mixin类(如UserDict.DictMixin)的经典Python替代方法相比,它们使isinstance(和issubclass)比过去(在2.5和以前)更具吸引力和普及性(在Python 2.6和以后的版本中),因此,相比之下,在最近的Python版本中,使检查类型相等成为一种更糟糕的做法以前是。

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

类型检查

isinstance(obj, Base)

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

isinstance(obj, (Base1, Base2))

而类型检查

type(obj) is Base

仅支持引用的类型。


作为旁注,is可能比

type(obj) == Base

因为类是单例的。

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

在Python中,通常需要允许参数使用任何类型,并将其视为预期的,如果对象的行为不符合预期,则会引发相应的错误。这就是所谓的多态性,也称为duck类型。

def function_of_duck(duck):
    duck.quack()
    duck.swim()

如果上面的代码有效,我们可以假设我们的论点是一只鸭子。因此,我们可以把其他的东西传递给duck的实际子类型:

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的原则,那么应该能够这样做,如果我们希望代码遵循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替换!以下内容:

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

回复评论:

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

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

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

同样,区别在于isinstance支持可以替换父类的子类,而不破坏程序,这是一个称为Liskov substitution的属性。

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

结论

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

下面是一个例子,其中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对于子类也是如此。

另请参见:How to compare type of an object in Python?

相关问题 更多 >