What Are the Differences Between Type() and Isinstance()

What are the differences between type() and isinstance()?

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.

Why use isinstance() instead of type()?

Let me give you a simple example why they can be different:

>>> class A(object): pass
>>> class B(A) : pass
>>> a = A()
>>> b = B()

So far, declared a variable A and a variable B derived from A.

>>> isinstance(a, A)
True
>>> type(a) == A
True

BUT

>>> isinstance(b, A)
True
>>> type(b) == A
False

Difference between isinstance and type in python

First check out all the great answers here.

type() simply returns the type of an object. Whereas, isinstance():

Returns true if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof.

Example:

class MyString(str):
pass

my_str = MyString()
if type(my_str) == 'str':
print 'I hope this prints'
else:
print 'cannot check subclasses'
if isinstance(my_str, str):
print 'definitely prints'

Prints:

cannot check subclasses
definitely prints

When to use type() instead of isinstanceof() in python?

They do two different things, you can't really compare them directly. What you've probably read is that you should prefer isinstance when checking the type of an object at runtime. But that isn't the only use-case for type (that is the use-case for isinstance, as its name implies).

What may not be obvious is that type is a class. You can think of "type" and "class" as synonymous. Indeed, it is the class of class objects, a metaclass. But it is a class just like int, float, list, dict etc. Or just like a use-defined class, class Foo: pass.

In its single argument form, it returns the class of whatever object you pass in. This is the form that can be used for type-checking. It is essentially equivalent to some_object.__class__.

>>> "a string".__class__
<class 'str'>
>>> type("a string")
<class 'str'>

Note:

>>> type(type) is type
True

You might also find this form useful if you ever wanted access to the type of an object itself for other reasons.

In its three-argument form, type(name, bases, namespace) it returns a new type object, a new class. Just like any other type constructor, just like list() returns a new list.

So instead of:

class Foo:
bar = 42
def __init__(self, val):
self.val = val

You could write:

def _foo_init(self, val):
self.val = val

Foo = type('Foo', (object,), {'bar':42, '__init__': _foo_init})

isinstance is a function which checks if... an object is an instance of some type. It is a function used for introspection.

When you want to introspect on the type of an object, usually you will probably use isintance(some_object, SomeType), but you might also use type(some_object) is SomeType. The key difference is that isinstance will return True if some_object.__class__ is precisely SomeType or any of the other types SomeType inherits from (i.e. in the method resolution order of SomeType, SomeType.mro()).

So, isinstance(some_object, SomeType) is essentially equivalent to some_object.__class__ is SomeType or some_object.__class__ in SomeType.mro()

Whereas if you use type(some_object) is SomeType, you are only asking some_object.__class__ is SomeType.

Here's a practical example of when you might want to use type instead of isinstance, suppose you wanted to distinguish between int and bool objects. In Python, bool inherits from int, so:

>>> issubclass(bool, int)
True

So that means:

>>> some_boolean = True
>>> isinstance(some_boolean, int)
True

but

>>> type(some_boolean) is int
False

Inheritance and isinstance

This transitive behavior is how it should work intuitively ...

>>> class Text:
...: pass
...:
...:
>>> class Book(Text):
...: pass
...:
...:
>>> class Novel(Book):
...: pass
...:
...:
>>> n = Novel()
>>> isinstance(n, Novel)
>>> True
>>> isinstance(n, Book)
>>> True
>>> isinstance(n, Text)
>>> True

... because a Novel is-a Novel, but also is-a Book and is-a Text.

If you want to know whether a class (or instance of a class) is a direct ancestor of another class, you can use the __subclasses__ method of class objects.

>>> Text.__subclasses__()
>>> [__main__.Book]
>>> Book.__subclasses__()
>>> [__main__.Novel]
>>> Novel.__subclasses__()
>>> []
>>>
>>> Novel in Text.__subclasses__()
>>> False
>>> type(n) in Text.__subclasses__()
>>> False
>>> Novel in Book.__subclasses__()
>>> True
>>> type(n) in Book.__subclasses__()
>>> True

edit: YourClass.__bases__ also gives you all direct parent classes.

>>> Novel.__bases__
>>> (__main__.Book,)

How does isinstance work in python for subclasses?

C() holds a reference to its type, C, and C has a tuple C.__mro__ of all its ancestors (including itself). The default type.__instancecheck__ looks through that tuple to see whether an object is an instance of a particular class.

If you follow the function calls down from type.__instancecheck__, you eventually end up in this loop:

for (i = 0; i < n; i++) {
if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b)
return 1;

Classes in Python do also have information about their subclasses - the C-level PyType_Ready function informs the base classes about a subclass while readying the subclass. However, searching the inheritance graph downward from the base class would produce pathologically slow behavior for cases where the second argument to isinstance has a lot of descendants.

isinstance(foo,bar) vs type(foo) is bar

if I'm checking what should ALWAYS BE A BASE OBJECT, what do I really lose from doing type is?

well, it's nice you give the full documented answer in your question, so your answer is you lose nothing! The only times where isinstance() is necessary is when checking inheritance of a given class compared to another, as you well said and referenced. type() shall be only used to check whether an instance is exactly of a given base type.



Related Topics



Leave a reply



Submit