这两个代码片段之间有什么区别?
使用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
的平等性则不然 (它要求类型的身份,拒绝子类型的实例,也就是子类)。
通常,在Python中,你当然希望你的代码支持继承(既然继承如此方便,那么阻止使用你的代码使用它就不好了!),所以isinstance
比检查type
的身份更不好,因为它无缝地支持继承。
这并不是说isinstance
是_好的,你要知道--它只是比检查类型的平等性_更不坏。
正常的,Pythonic的,首选的解决方案是"duck typing"。
试着使用参数_好像它是某一类型,在 "try"/"except "语句中进行,捕捉所有可能出现的异常,如果参数事实上不是该类型(或任何其他类型,很好地模仿它;-),在 "except "子句中,尝试其他东西(使用参数"好像"
它是其他类型的)。)
然而,basestring
是的,是一个相当特殊的情况--一个内置的类型,它的存在**只是为了让你使用isinstance
(str
和unicode
都是basestring
的子类)。
字符串是序列(你可以在它们上面循环、索引、切分它们,......),但你通常想把它们当作"标量"。
类型--用一种方式处理所有类型的字符串(也许还有其他标量类型,即那些你不能循环的类型),用另一种方式处理所有的容器(list、set、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][1]被接受,并从Python 2.6和3.0开始实现。
PEP明确指出,虽然ABCs经常可以替代鸭式输入法,但一般来说没有太大的压力(参见[这里][2])。
然而,在最近的 Python 版本中实现的 ABC 确实提供了额外的好东西。
isinstance
(和issubclass
)现在不仅仅意味着 "[一个实例]派生类"。
(特别是,任何类都可以用"注册"。
注册,这样它就会显示为一个子类,而它的实例则显示为ABC的实例)。)
而ABC也可以通过模板方法设计模式的应用,以一种非常自然的方式为实际的子类提供额外的便利(关于TM DP的更多内容,请参见[这里][3]和[这里][4][[第二部分]],在Python中一般地和具体地,独立于ABC)。
关于Python 2.6中提供的ABC支持的底层机制,参见[这里][5]。 关于它们的 3.1 版本,非常相似,参见 [这里][6]。 在这两个版本中,标准库模块[collections]7提供了几个有用的ABC。
为了这个答案的目的,保留 ABCs 的关键之处 (除了可以说是 TM DP 功能更自然的位置之外,与经典的 Python 替代品 mixin 类,如 [UserDict.DictMixin][9]相比),它们使 isinstance
(和issubclass
)比过去 (在 2.5 和之前) 更有吸引力和更普遍 (在 Python 2.6 和未来),因此,相比之下,在最近的 Python 版本中,检查类型平等比过去更糟糕。
[1]: http://www.python.org/dev/peps/pep-3119/ [2]: http://www.python.org/dev/peps/pep-3119/#abcs-vs-duck-typing [3]: http://en.wikipedia.org/wiki/Template_method_pattern [4]: http://www.catonmat.net/blog/learning-python-design-patterns-through-video-lectures/ [5]: http://docs.python.org/library/abc.html [6]: http://docs.python.org/3.1/library/abc.html [7]: http://docs.python.org/3.1/library/collections.html#module-collections [8]: http://docs.python.org/library/collections.html#module-collections [9]: http://docs.python.org/library/userdict.html?highlight=userdict#UserDict.DictMixin
这里有一个例子,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中
isinstance()
和type()
之间的区别?
类型检查
isinstance(obj, Base)
允许子类的实例和多个可能的基。
isinstance(obj, (Base1, Base2))
而用
type(obj) is Base
只支持被引用的类型。
顺便说一句,"是 "可能比 "是 "更合适。
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()
的区别?请允许我演示一下它们的区别。
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][1]的原则,即子类型可以被替换成类型的话,我们应该可以做到这一点),我们的代码就会崩溃。
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
。
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 "进行类型检查--除非你真的需要知道一个实例的精确类。
[1]: http://en.wikipedia.org/wiki/Liskov_substitution_principle
后者是首选,因为它能正确处理子类。事实上,你的例子可以写得更简单,因为isinstance()
'的第二个参数可以是一个元组。
if isinstance(b, (str, unicode)):
do_something_else()
或者,使用basestring
抽象类。
if isinstance(b, basestring):
do_something_else()
根据python文档,这里有一个声明。
8.15. types - Names for built-in types
从Python 2.2开始,内置的 工厂函数,如
int()
和str()
也是内建类型的名称。 相应的类型。
所以isinstance()
应该优先于type()
。
对于真正的差异,我们可以在 "code "中找到,但我找不到 "isinstance() "默认行为的实现。
不过我们可以根据abc._/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
我得到这个结论。 对于`类型'。
# 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 "和 "absolutely import",使用 "absolutely import "从project_dir(由sys.path
添加的)