Thursday, October 15, 2020

Sebastian Witowski: 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 behave differently in each case. That’s what the SMTP.sendmail() from the smtplib does. It checks if the recipient 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.

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 int"
2000000 loops, best of 5: 102 nsec per loop

$ python -m timeit -s "variable = 'hello'" "isinstance(variable, str)"
5000000 loops, best of 5: 72.8 nsec per loop

type is 40% slower (102/72.8 = 1.40).

We could use type(variable) == str instead. It will work, 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.
  • == is slower:
    $ python -m timeit -s "variable = 'hello'" "type(variable) == str"
    2000000 loops, best of 5: 114 nsec per loop
    

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 if variable is of a type str.
  • 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 the str 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 of str?

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 often the preferred way to compare types. It’s not only faster but also considers inheritance (which is often the desired behavior). type should be used when you want to explicitly check that a given variable is of a specific type (and not its subclass). And when you use type, 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.



from Planet Python
via read more

No comments:

Post a Comment

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...