What are the differences between type() and isinstance()? [duplicate]
Asked Answered
T

8

1558

What are the differences between these two code snippets?

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()
Tail answered 11/10, 2009 at 3:50 Comment(2)
Note:If it is not str and unicode (where you can just check basestring), you can use a tuple for checking against multiple types. To check if something is int or str use isinstance(something, (int, str)).Salema
type() returns the type of the object you put in as an argument, and is usually not useful unless compared with a real type (such as type(9) == int). isinstance() returns a boolean - true or false - based on whether the object is of given type. isinstance is usually more elegant to use rather than write a cluttered equality check, for most cases.Memoirs
A
1526

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.

Aubergine answered 11/10, 2009 at 4:31 Comment(8)
'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"' This is a rather limited view: there are very good cases for using isinstance() in, say, an interpreter where the types reflect the grammar. Being "Pythonic" isn't everything!Mottle
basestring is not available in Python 3.Ess
@GeneCallahan, because there are very good cases, doesn't mean what was said isn't a good general rule. I agree that checking for type ahead of time definitely has its place but letting the ducks quack should cover most cases more flexibly and efficiently.Alarmist
@erobertc, according to What's New in Python 3.0, "The built-in basestring abstract type was removed. Use str instead."Set
The same goes for the unicode - type. "Python 3.0 uses the concepts of text and (binary) data instead of Unicode strings and 8-bit strings. All text is Unicode; however encoded Unicode is represented as binary data."Waylon
I never understand the adjectives better and worse when there is no objective. How can something be better if you don't know what for? isinstance is not better if you don't want to check for subclasses too. It just does a different thing.Tippet
@EduardoPignatelli: For subjective uses like this, "better" really just means "which should be the default when you don't have a strong reason to prefer a specific approach?". Here, if you don't have a known requirement to exclude subclasses, default to isinstance even if you don't know of any subclasses you'd want to handle, because it's more likely to work if a third party using your code wants to pass along their own subclass of the type you recognize; it might not work if the subclass violates expectations, but it definitely won't work if you refuse to handle the subclass at all.Tetramethyldiarsine
Similarly, duck-typing is "better" than isinstance checking if you don't have cases where the type-checking is strictly necessary. There are cases where it is. A common case to avoid duck-typing would be where you have steps X and Y to execute, where Y depends on X having completed. X allocates some expensive resource or performs some hard-to-revert action, but only Y will fail if duck-typing isn't satisfied. You want to verify that the input is Y-compliant before doing X, and isinstance checking might be the only reasonable way to do it, even if it means rejecting maybe-valid input.Tetramethyldiarsine
P
446

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?

Pleadings answered 11/10, 2009 at 3:58 Comment(3)
It's a good example of how they work differently, but I just ran into a case where I specifically needed type() and not isinstance(). One is not better; they are for different things.Tortoni
Please can you tell me - why have you used == instead of using "is"?Time
@Time "is will return True if two variables point to the same object, == if the objects referred to by the variables are equal." See this SO post. I'm not sure if it matters in the context of this answer.Esker
L
132

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.

Lionellionello answered 6/2, 2015 at 4:13 Comment(1)
If you have your_module.py where you check for isinstance(instance, y) and use from v.w.x import y, and you import that check, but when you instantiate instance you use from x import y instead of how y was imported in your_module.py, the isinstance check will fail, even though it's the same class.Brat
D
70

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()
Dorser answered 11/10, 2009 at 3:54 Comment(0)
D
23

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.

Drawing answered 25/5, 2019 at 4:33 Comment(0)
D
15

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().

Donniedonnish answered 11/10, 2009 at 3:59 Comment(0)
C
2

The differences between type() and isinstance()

type() -> returns the type of the object

isinstance() -> returns a boolean

Generally speaking isinstance is a 'more' elegant way of checking if an object is of a certain "type" (since you are aware of the Inheritance chain).

On the other hand, if you are not aware of the inheritance chain and you need to be pick, go for type(x) == ...

Another interesting case for type is when you check for 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
Chloromycetin answered 19/5, 2022 at 10:0 Comment(0)
H
1

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)

Hoppe answered 10/5, 2018 at 8:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.