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
很好, isinstance
你 - 这比检查类型的平等性要差。 正常的Pythonic首选解决方案几乎总是“鸭子打字”:尝试使用参数,就好像它是某种期望类型一样,在try
/ except
语句中执行,以捕获所有可能出现的异常,如果该参数实际上不是事实(或者其他任何类型的鸭子模仿它;-),并且在except
语句中,尝试其他的东西(使用参数“好像”它是某种其他类型)。
然而, basestring
是一个非常特殊的情况 - 内建类型只存在于isinstance
( str
和unicode
子类basestring
)中。 字符串是序列(你可以遍历它们,为它们建立索引,对它们进行分片......),但是你通常希望把它们当作“标量”类型 - 它有点不方便(但是相当频繁的用例)来处理各种字符串(也可能是其他标量类型,也就是你无法循环的标量类型),所有容器(列表,集合,字典,...)以另一种方式,而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明确指出,虽然ABC通常可以代替鸭子打字,但通常没有很大的压力要做(见这里)。 然而,在最近的Python版本中实现的ABCs却提供了额外的好处: isinstance
(和issubclass
)现在不仅仅意味着“[派生类的实例]”(特别是,任何类都可以用ABC注册,它将显示为一个子类,并将它的实例显示为ABC的实例); 并且ABCs还可以通过模板方法设计模式应用程序以非常自然的方式为实际的子类提供额外的便利(有关TM DP的更多信息,请参见此处和此处[部分II]],通常和特别是Python,独立于ABCs) 。
有关Python 2.6中提供的ABC支持的基本机制,请参阅此处; 对于他们的3.1版本,非常相似,请看这里。 在这两个版本中,标准库模块集合(这是3.1版本 - 对于非常类似的2.6版本,请参见此处)提供了几个有用的ABC。
出于这个答案的目的,关于ABCs的关键之处在于它们使用isinstance
(和issubclass
)(与可用于TM DP功能的更自然的布局相比,与经典的Python mixin类如UserDict.DictMixin 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 dict:
# we're only going to test for dicts for now
raise ValueError('only dicts are supported for now')
如果我们试图在一个字典是的子类通过dict
(因为我们应该可以,如果我们希望我们的代码遵循Liskov替换原则,即亚型可以取代类型)我们的代码休息!:
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
,喜欢与类型检查isinstance
-除非你真的需要知道确切类的实例。
上一篇: What are the differences between type() and isinstance()?
下一篇: What is the difference between old style and new style classes in Python?