这两个代码片段有什么区别?
使用 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()
str
和 unicode
(您可以只检查 basestring
),您可以使用元组来检查多种类型。要检查 something
是 int
还是 str
,请使用 isinstance(something, (int, str))
。
type()
返回您作为参数输入的对象的类型,除非与实际类型(例如 type(9) == int
)进行比较,否则通常没有用处。 isinstance()
根据对象是否为给定类型返回布尔值 - true 或 false。在大多数情况下,isinstance
通常使用起来更优雅,而不是编写混乱的相等检查。
总结其他(已经很好!)答案的内容,isinstance
迎合继承(派生类的实例也是基类的实例),同时检查 { 2} 没有(它要求类型的标识并拒绝子类型的实例,AKA 子类)。
通常,在 Python 中,您当然希望您的代码支持继承(由于继承非常方便,因此阻止使用您的代码使用它会很糟糕!),所以 isinstance
比检查 {2 的身份更糟糕}s 因为它无缝地支持继承。
请注意,这并不是说 isinstance
好——它只是不如检查类型的相等性。正常的、Pythonic 的首选解决方案几乎总是“鸭子类型”:尝试使用参数 就好像 它是某种所需的类型,在 try
/except
语句中捕获所有如果参数实际上不是该类型(或任何其他类型很好地模仿它;-),则可能出现异常,并且在 except
子句中尝试其他内容(使用参数“好像”它是其他类型)。
basestring
是,然而,一个非常特殊的情况 - 存在的内置类型仅允许您使用 isinstance
(str
和 unicode
子类 { 1})。字符串是序列(你可以循环它们,索引它们,切片它们,......),但你通常希望将它们视为“标量”类型——处理各种类型的字符串有点不方便(但相当频繁的用例)字符串(可能还有其他标量类型,即不能循环的类型)以一种方式,所有容器(列表、集合、字典等)以另一种方式,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 通常可以替代鸭式打字,但这样做通常没有太大的压力(参见 here)。然而,在最近的 Python 版本中实现的 ABC 确实提供了额外的好处:isinstance
(和 issubclass
)现在不仅仅意味着“派生类的[一个实例]”(特别是,任何类都可以用一个 ABC,以便它将显示为子类,并将其实例显示为 ABC 的实例);并且 ABC 还可以通过模板方法设计模式应用程序以非常自然的方式为实际子类提供额外的便利(请参阅 here 和 here [[part II]] 了解更多关于 TM DP 的信息,一般来说,特别是在 Python 中,独立于 ABC)。
有关 Python 2.6 中提供的 ABC 支持的基本机制,请参阅 here;对于它们的 3.1 版本,非常相似,请参见 here。在这两个版本中,标准库模块 collections(即 3.1 版本 — 对于非常相似的 2.6 版本,请参阅 here)提供了几个有用的 ABC。
就这个答案而言,保留关于 ABC 的关键(除了 TM DP 功能可以说是更自然的位置,与诸如 UserDict.DictMixin 之类的 mixin 类的经典 Python 替代方案相比)是它们使 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
也适用于子类。
另请参阅:How to compare type of an object in Python?
type
,请改用 isinstance
”。例如,我想要的正是 type()
检查,但由于这个原因,我被误导了一小段时间(并且不得不进行一点调试)。
type()
而不是 isinstance()
的情况。一个不是更好;他们是为了不同的事情。
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() 的区别?
请允许我证明其中的区别:
类型
假设如果您的函数获得某种类型的参数(构造函数的常见用例),您需要确保某种行为。如果您检查这样的类型:
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
,我们可以支持 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 可以用来检查多个类,使用 type(obj) in (A, B, C)
是的,您可以测试类型的相等性,但不是上面的,而是使用控制流的多个基础,除非您明确只允许这些类型:
isinstance(obj, (A, B, C))
同样,不同之处在于 isinstance
支持可以在不破坏程序的情况下替换父类的子类,这种属性称为 Liskov 替换。
不过,更好的是,反转您的依赖关系并且根本不检查特定类型。
结论
因此,由于我们希望支持替换子类,因此在大多数情况下,我们希望避免使用 type
进行类型检查,而更喜欢使用 isinstance
进行类型检查 - 除非您确实需要知道实例的精确类。
isinstance(instance, y)
并使用 from v.w.x import y
,然后导入该检查,但是当您实例化 instance
时,您使用 from x import y
而不是在 your_module.py 中导入 y 的方式,isinstance check 会失败,即使它是同一个类。
后者是首选,因为它将正确处理子类。事实上,您的示例可以更轻松地编写,因为 isinstance()
的第二个参数可能是一个元组:
if isinstance(b, (str, unicode)):
do_something_else()
或者,使用 basestring
抽象类:
if isinstance(b, basestring):
do_something_else()
一个实际的用法差异是它们如何处理 booleans
:
True
和 False
只是在 python 中表示 1
和 0
的关键字。因此,
isinstance(True, int)
和
isinstance(False, int)
两者都返回 True
。两个布尔值都是整数的实例。然而,type()
更聪明:
type(True) == int
返回 False
。
根据python文档,这里有一个声明:
8.15。 types - 内置类型的名称 从 Python 2.2 开始,内置工厂函数(如 int() 和 str())也是相应类型的名称。
因此,isinstance()
应优先于 type()
。
对于真正的差异,我们可以在 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
,使用 project_dir 中的 absolutely import
(由 sys.path
添加)
type() 和 isinstance() 的区别
type()
->返回对象的类型
isinstance()
->返回一个布尔值
一般来说,isinstance
是一种“更”优雅的检查对象是否属于某种“类型”的方式(因为您知道继承链)。
另一方面,如果您不了解继承链并且需要挑选,请选择 type(x) == ...
type
的另一个有趣案例是当您检查 bool
----Case bool----
print(type(True) == int) # False
print(type(False) == int) # False
print(type(True) == bool) # True
print(type(False) == bool) # True
print(isinstance(True, int)) # True
print(isinstance(True, int)) # True
----Case inheritance----
class A:
x=1
class B(A):
x=2
class C(B):
x=3
var1 = A()
var2 = B()
var3 = C()
print(type(var1)) # <class '__main__.A'>
print(type(var1) == A) # True
print(type(var2) == A) # False
print(type(var3) == A) # False
print(isinstance(var1, A)) # True
print(isinstance(var2, A)) # True
print(isinstance(var3, A)) # True
print(type(var2)) # <class '__main__.B'>
print(type(var1) == B) # False
print(type(var2) == B) # True
print(type(var3) == B) # False
print(isinstance(var1, B)) # False
print(isinstance(var2, B)) # True
print(isinstance(var3, B)) # True
print(type(var3)) # <class '__main__.C'>
print(type(var1) == C) # False
print(type(var2) == C) # False
print(type(var3) == C) # True
print(isinstance(var1, C)) # False
print(isinstance(var2, C)) # False
print(isinstance(var3, C)) # True
isinstance
会不更好。它只是做了一个不同的事情。