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

问题: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()

What are the differences between these two code fragments?

Using type():

import types

if type(a) is types.DictType:
    do_something()
if type(b) in types.StringTypes:
    do_something_else()

Using isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()

回答 0

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

通常,在Python中,当然,您希望您的代码支持继承(由于继承非常方便,因此停止使用您的代码来使用它会很糟糕!),这isinstance比检查types的身份要糟糕得多,因为它无缝地支持s遗产。

这并不是说isinstance不错的,你要知道,它只是不那么糟糕不是检查的类型平等。正常的,Python式的首选解决方案几乎总是“鸭式输入”:尝试使用参数,就好像它是某个所需的类型一样,在try/ except语句中进行处理,以捕获如果参数实际上不是该参数可能会出现的所有异常类型(或其他可以模仿它的其他类型;-),然后在except子句中尝试其他操作(使用参数“好像”是其他类型)。

basestring ,但是,相当多的特殊情况,一个内建存在类型让你使用isinstance(包括strunicode子类basestring)。字符串是序列(您可以对它们进行循环,对其进行索引,对其进行切片等),但是您通常希望将它们视为“标量”类型-处理各种类型的字符串有点不方便(但在相当频繁的情况下)字符串(可能还有其他标量类型,即您不能循环的类型),所有容器(列表,集合,字典,…)的另一种方法,basestring加上这些isinstance可以帮助您做到这一点–总体结构成语是这样的:

if isinstance(x, basestring)
  return treatasscalar(x)
try:
  return treatasiter(iter(x))
except TypeError:
  return treatasscalar(x)

您可以说这basestring是一个抽象基类(“ ABC”)-它没有为子类提供具体功能,而是作为“标记”存在,主要用于isinstance。自从引入PEP 3119(引入了它的概括)以来,该概念显然在Python中正在不断发展。自从PEP 3119以来,该概念已从Python 2.6和3.0开始实施。

PEP清楚地表明,尽管ABC通常可以代替鸭类打字,但这样做通常没有很大的压力(请参见此处)。但是,在最新的Python版本中实现的ABC确实提供了额外的好处:(isinstanceissubclass)现在的含义不仅仅是“派生类的一个实例”(特别是,任何类都可以在ABC中“注册”,以便它可以显示为子类,其实例显示为ABC实例);并且ABC还可以通过模板方法设计模式应用程序以一种非常自然的方式为实际子类提供额外的便利(有关TM DP的更多信息,请参见此处此处的 [[Part II]],有关Python的更多信息,特别是在Python中,与ABC无关) 。

有关Python 2.6中提供的ABC支持的基本机制,请参见此处;其3.1版本非常相似,请参见此处。在这两个版本中,标准库模块集合(即3.1版本,对于非常相似的2.6版本,请参见此处)都提供了一些有用的ABC。

出于这个答案的目的,保留ABC的关键是(与TM DP混合类​​的经典Python替代类(例如UserDict.DictMixin相比,TM DP功能可以说是更自然的放置))是它们使isinstance(和issubclass)具有更多优势(在Python 2.6及更高版本中)比以前(在2.5及更低版本中)更具吸引力和普遍性,因此,相比之下,使类型相等性检查在最近的Python版本中比以前更加糟糕。

To summarize the contents of other (already good!) answers, isinstance caters for inheritance (an instance of a derived class is an instance of a base class, too), while checking for equality of type does not (it demands identity of types and rejects instances of subtypes, AKA subclasses).

Normally, in Python, you want your code to support inheritance, of course (since inheritance is so handy, it would be bad to stop code using yours from using it!), so isinstance is less bad than checking identity of types because it seamlessly supports inheritance.

It’s not that isinstance is good, mind you—it’s just less bad than checking equality of types. The normal, Pythonic, preferred solution is almost invariably “duck typing”: try using the argument as if it was of a certain desired type, do it in a try/except statement catching all exceptions that could arise if the argument was not in fact of that type (or any other type nicely duck-mimicking it;-), and in the except clause, try something else (using the argument “as if” it was of some other type).

basestring is, however, quite a special case—a builtin type that exists only to let you use isinstance (both str and unicode subclass basestring). Strings are sequences (you could loop over them, index them, slice them, …), but you generally want to treat them as “scalar” types—it’s somewhat incovenient (but a reasonably frequent use case) to treat all kinds of strings (and maybe other scalar types, i.e., ones you can’t loop on) one way, all containers (lists, sets, dicts, …) in another way, and basestring plus isinstance helps you do that—the overall structure of this idiom is something like:

if isinstance(x, basestring)
  return treatasscalar(x)
try:
  return treatasiter(iter(x))
except TypeError:
  return treatasscalar(x)

You could say that basestring is an Abstract Base Class (“ABC”)—it offers no concrete functionality to subclasses, but rather exists as a “marker”, mainly for use with isinstance. The concept is obviously a growing one in Python, since PEP 3119, which introduces a generalization of it, was accepted and has been implemented starting with Python 2.6 and 3.0.

The PEP makes it clear that, while ABCs can often substitute for duck typing, there is generally no big pressure to do that (see here). ABCs as implemented in recent Python versions do however offer extra goodies: isinstance (and issubclass) can now mean more than just “[an instance of] a derived class” (in particular, any class can be “registered” with an ABC so that it will show as a subclass, and its instances as instances of the ABC); and ABCs can also offer extra convenience to actual subclasses in a very natural way via Template Method design pattern applications (see here and here [[part II]] for more on the TM DP, in general and specifically in Python, independent of ABCs).

For the underlying mechanics of ABC support as offered in Python 2.6, see here; for their 3.1 version, very similar, see here. In both versions, standard library module collections (that’s the 3.1 version—for the very similar 2.6 version, see here) offers several useful ABCs.

For the purpose of this answer, the key thing to retain about ABCs (beyond an arguably more natural placement for TM DP functionality, compared to the classic Python alternative of mixin classes such as UserDict.DictMixin) is that they make isinstance (and issubclass) much more attractive and pervasive (in Python 2.6 and going forward) than they used to be (in 2.5 and before), and therefore, by contrast, make checking type equality an even worse practice in recent Python versions than it already used to be.


回答 1

这是一个无法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中比较对象的类型?

Here’s an example where isinstance achieves something that type cannot:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

in this case, a truck object is a Vehicle, but you’ll get this:

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.

In other words, isinstance is true for subclasses, too.

Also see: How to compare type of an object in Python?


回答 2

isinstance()type()Python 之间的区别?

进行类型检查

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对象可以由字典记录构造。在这种情况下,您的代码需要知道它获取的参数类型,以便它可以正确处理它。

所以,要回答这个问题:

isinstance()type()Python 之间的区别?

请允许我展示一下区别:

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替换!:

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-进行类型检查-除非您确实需要知道实例的确切类。

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

Type-checking with

isinstance(obj, Base)

allows for instances of subclasses and multiple possible bases:

isinstance(obj, (Base1, Base2))

whereas type-checking with

type(obj) is Base

only supports the type referenced.


As a sidenote, is is likely more appropriate than

type(obj) == Base

because classes are singletons.

Avoid type-checking – use Polymorphism (duck-typing)

In Python, usually you want to allow any type for your arguments, treat it as expected, and if the object doesn’t behave as expected, it will raise an appropriate error. This is known as polymorphism, also known as duck-typing.

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

If the code above works, we can presume our argument is a duck. Thus we can pass in other things are actual sub-types of duck:

function_of_duck(mallard)

or that work like a duck:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

and our code still works.

However, there are some cases where it is desirable to explicitly type-check. Perhaps you have sensible things to do with different object types. For example, the Pandas Dataframe object can be constructed from dicts or records. In such a case, your code needs to know what type of argument it is getting so that it can properly handle it.

So, to answer the question:

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

Allow me to demonstrate the difference:

type

Say you need to ensure a certain behavior if your function gets a certain kind of argument (a common use-case for constructors). If you check for type like this:

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')

If we try to pass in a dict that is a subclass of dict (as we should be able to, if we’re expecting our code to follow the principle of Liskov Substitution, that subtypes can be substituted for types) our code breaks!:

from collections import OrderedDict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

raises an error!

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
ValueError: argument must be a dict

isinstance

But if we use isinstance, we can support 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')]))

returns OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

Abstract Base Classes

In fact, we can do even better. collections provides Abstract Base Classes that enforce minimal protocols for various types. In our case, if we only expect the Mapping protocol, we can do the following, and our code becomes even more flexible:

from collections import Mapping

def foo(a_dict):
    if not isinstance(a_dict, Mapping):
        raise ValueError('argument must be a dict')
    return a_dict

Response to comment:

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

Yes, you can test for equality of types, but instead of the above, use the multiple bases for control flow, unless you are specifically only allowing those types:

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

The difference, again, is that isinstance supports subclasses that can be substituted for the parent without otherwise breaking the program, a property known as Liskov substitution.

Even better, though, invert your dependencies and don’t check for specific types at all.

Conclusion

So since we want to support substituting subclasses, in most cases, we want to avoid type-checking with type and prefer type-checking with isinstance – unless you really need to know the precise class of an instance.


回答 3

首选后者,因为它将正确处理子类。实际上,由于isinstance()的第二个参数可能是元组,因此您的示例编写起来甚至更加容易:

if isinstance(b, (str, unicode)):
    do_something_else()

或者,使用basestring抽象类:

if isinstance(b, basestring):
    do_something_else()

The latter is preferred, because it will handle subclasses properly. In fact, your example can be written even more easily because isinstance()‘s second parameter may be a tuple:

if isinstance(b, (str, unicode)):
    do_something_else()

or, using the basestring abstract class:

if isinstance(b, basestring):
    do_something_else()

回答 4

根据python文档,这是一条语句:

8.15。类型-内置类型的名称

从Python 2.2开始,内置的工厂函数(例如int()和) str()也是相应类型的名称。

所以isinstance()应该优先于type()

According to python documentation here is a statement:

8.15. types — Names for built-in types

Starting in Python 2.2, built-in factory functions such as int() and str() are also names for the corresponding types.

So isinstance() should be preferred over type().


回答 5

实际用法的区别在于它们如何处理booleans

TrueFalse只是关键字,平均10Python编写的。从而,

isinstance(True, int)

isinstance(False, int)

都回来了True。两个布尔值都是整数的实例。type()但是,它更聪明:

type(True) == int

返回False

A practical usage difference is how they handle booleans:

True and False are just keywords that mean 1 and 0 in python. Thus,

isinstance(True, int)

and

isinstance(False, int)

both return True. Both booleans are an instance of an integer. type(), however, is more clever:

type(True) == int

returns False.


回答 6

对于真正的差异,我们可以在中找到它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)})

顺便说一句:最好不要混用use relative and absolutely import,而要使用absolutely importproject_dir(由添加sys.path

For the real differences, we can find it in code, but I can’t find the implement of the default behavior of the isinstance().

However we can get the similar one abc.__instancecheck__ according to __instancecheck__.

From above abc.__instancecheck__, after using test below:

# 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

I get this conclusion, For type:

# according to `abc.__instancecheck__`, they are maybe different! I have not found negative one 
type(INSTANCE) ~= INSTANCE.__class__
type(CLASS) ~= CLASS.__class__

For 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)})

BTW: better not to mix use relative and absolutely import, use absolutely import from project_dir( added by sys.path)