type() vs. isinstance()
Python is a dynamically typed language. A variable, initially created as a string, can be later reassigned to an integer or a float. And the interpreter won't complain:
name = "Sebastian"
# Dynamically typed language lets you do this:
name = 42
name = None
name = Exception()
It's quite common to see code that checks variable's type. Maybe you want to accept both a single element and a list of items and act differently in each case. That's what the SMTP.sendmail() from the smtplib does. It checks if the recipient
is a string or a list of strings and sends one or more emails.
About the "Writing Faster Python" series
"Writing Faster Python" is a series of short articles discussing how to solve some common problems with different code structures. I run some benchmarks, discuss the difference between each code snippet, and finish with some personal recommendations.
Are those recommendations going to make your code much faster? Not really.
Is knowing those small differences going to make a slightly better Python programmer? Hopefully!
You can read more about some assumptions I made, the benchmarking setup, and answers to some common questions in the Introduction article. And you can find most of the code examples in this repository.
To check the type of a variable, you can use either type() or isinstance() built-in function. Let's see them in action:
>>> variable = "hello"
>>> type(variable) is str
True
>>> isinstance(variable, str)
True
Let's compare both methods' performance:
$ python -m timeit -s "variable = 'hello'" "type(variable) is str"
5000000 loops, best of 5: 52.1 nsec per loop
$ python -m timeit -s "variable = 'hello'" "isinstance(variable, str)"
10000000 loops, best of 5: 35.5 nsec per loop
type
is around 40% slower (52.1/35.5≈1.47).
We could use type(variable) == str
instead, but it's a bad idea. ==
should be used when you want to check the value of a variable. We would use it to see if the value of variable
is equal to "hello"
. But when we want to check if variable
is a string, is
operator is more appropriate. For a more detailed explanation of when to use one or the other, check this article.
Python 3.11 update
In Python 3.11, the difference between the two above code snippets becomes almost negligible:
# Python 3.11.0
$ python -m timeit -s "variable = 'hello'" "type(variable) is str"
20000000 loops, best of 5: 12.3 nsec per loop
$ python -m timeit -s "variable = 'hello'" "isinstance(variable, str)"
20000000 loops, best of 5: 12.7 nsec per loop
That's around a 3% difference. But the following recommendations are still valid no matter which version of Python you are using.
Difference between isinstance
and type
Speed is not the only difference between these two functions. There is actually an important distinction between how they work:
type
only returns the type of an object (its class). We can use it to check ifvariable
is of a typestr
.isinstance
checks if a given object (first parameter) is:- an instance of a class specified as a second parameter. For example, is
variable
an instance of thestr
class? - or an instance of a subclass of a class specified as a second parameter. In other words - is
variable
an instance of a subclass ofstr
?
- an instance of a class specified as a second parameter. For example, is
What does it mean in practice? Let's say we want to have a custom class that acts like a list but has some additional methods. So we might subclass the list
type and add custom functions inside:
class MyAwesomeList(list):
# Add additional functions here
But now the type
and isinstance
return different results if we compare this new class to a list!
>>> my_list = MyAwesomeList()
>>> type(my_list) is list
False
>>> isinstance(my_list, list)
True
We get different results because isinstance
checks if my_list
is an instance of list
(it's not) or a subclass of list
(it is, because MyAwesomeList
is a subclass of list
). If you forget about this difference, it can lead to some subtle bugs in your code.
A better way to create a custom list-like class
If you really need to create a custom class that behaves like a list but has some additional features, check out the collections module. It contains classes like UserList
, UserString
, or UserDictionary
. They are specifically designed to be subclassed when you want to create something that acts like a list, string, or a dictionary. If you try to subclass the list
class, you might quickly fall into a rabbit hole of patching and reimplementing the existing methods just to make your subclass work as expected. Trey Hunner as a good article explaining this problem called "The problem with inheriting from dict and list in Python".
Conclusions
isinstance
is usually the preferred way to compare types. It's not only faster but also considers inheritance, which is often the desired behavior. In Python, you usually want to check if a given object behaves like a string or a list, not necessarily if it's exactly a string. So instead of checking for string and all it's custom subclasses, you can just use isinstance
.
On the other hand, when you want to explicitly check that a given variable is of a specific type (and not its subclass) - use type
. And when you use it, use it like this: type(var) is some_type
not like this: type(var) == some_type
.
And before you start checking types of your variables everywhere throughout your code, check out why "Asking for Forgiveness" might be a better way.