The best answers to the question “What are the differences between type() and isinstance()?” in the category Dev.
What are the differences between these two code fragments?
import types if type(a) is types.DictType: do_something() if type(b) in types.StringTypes: do_something_else()
if isinstance(a, dict): do_something() if isinstance(b, str) or isinstance(b, unicode): do_something_else()
Here’s an example where
isinstance achieves something that
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?
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
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
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
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:
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
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.
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()
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:
or that work 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:
type() in Python?
Allow me to demonstrate the difference:
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
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')]))
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.
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.