Python type hints: a guide

Python type hints: a guide

- 5 mins

Introduction

Python annotations has been around since Python 3.0. They were first introduced without any specific purpose in PEP 3107, but it was not until Python 3.5 and PEP 484 that their real purpose was defined: type hinting.

Type hints are used to specify types in functions signatures and variable definitions, to avoid unintended error when programming in a dynamic-typed language such as python 🐍.

Why are they useful

People usually love Python because of its clear syntax, and lack of verbosity. When Type hints were introduced through the typing package, they were designed to be flexible enough:

  1. To allow a gradual usage throughout your code base.
  2. To allow users decide the level of verbosity:
from typing import Dict
from typing import List

# Option A
def foo(param: list):
    pass

# Option B
def foo(param: List[dict]):
    pass

# Option C
def foo(param: List[Dict[str, int]]):
    pass

In addition, these type annotations do not affect run-time execution, as they are only checked when the code is seating idle, and a static type analyzer is run (more on type analyzers later on).

Be aware, type annotations do not prevent wrong types to be used at run-time, however, when a codebase has concrete enough type annotations, and it is compliant with a mature enough static type analyzer, you can be fairly sure it won’t happen 😌.

How you should use them

As mentioned before, flexibility is the key word here. You should use them in the way that you feel the most comfortable.

In my opinion, they should be always be used in functions and class attributes, but not on common variables. The reason for this logic is that classes and functions have a “contract” nature inherent to them, that scope specific variables do not.

Static type analyzers

Defining all those type hints is great, and it could help new programmers to faster understand what a given function is doing. However, they need a static analyzer tool to be truly useful.

An additional argument to show the need for a static analyzer, is the fact that not all IDEs have a type watcher tool built in. I personally use PyCharm, which highlights as warning any type misaligned it encounters, but there are others not-so-complete IDEs that do not provide these automatic checks (VSCode, SublimeText…)

What type analyzers exist

There are several static type analyzers for Python out there. From a quick search on May 2020, it seems there are four of them getting more traction across the community (coincidentally created by the biggest players in the tech. world):

It is cool to discover that community traction is one of those fields where Zipf’s law also applies! For more context, please watch Vsauce Zipf law video, it is truly amazing.

How to choose a type analyzer

All the candidates from the previous section seem mature enough (due to the number of starts), and well-supported enough (due to the organization holding them), to be chosen as our primary option without any regrets.

In my particular case, I considered the following criteria:

After considering all the candidates, I decided for Mypy because:

My choice: Mypy

This section considers that you have also chosen Mypy as your static analyzer of choice. If not, then you have reached the end of this post.

Configuring Mypy

When configuring how to run Mypy, I recommend using the following flags:

Some considerations

Even if Mypy has reached a relatively good maturity level, there are still some open issues which may annoy you when defining advanced typing hints:

Finally, the use of its pre-commit hook is very valuable to ensure that our mypy-compliant project remains that way in the future. Just consider that you may need to use the pass_filenames: false option if there are multiple modules with the same name.

An example Mypy pre-commit hook could look like this:

-   repo: https://github.com/pre-commit/mirrors-mypy
    rev: v0.770
    hooks:
    -   id: mypy
        name: "Python types analyzer (source)"
        args: ["--allow-redefinition", "--ignore-missing-imports", "--cache-dir=/dev/null", "src"]
        language: python
        pass_filenames: false

Conclusions

Python type hints are improve the quality of any Python projects, and you should definitely use them when starting a project from scratch.

For already existing project, the approach to add hints needs to be more flexible, as it could be unfeasible to type everything on one sitting. Thankfully, static type analyzers allow us to flexibly define them.

Choose your favourite static analyzer and start coding 🚀.

Sinclert Pérez

Sinclert Pérez

Sr Software Engineer @ Shopify

rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora