Earlier this month, mypy released version 1.0.
In case you were not aware, python allows you to specify "type hints" whereby, much like in static typed languages, you can declare the data type of a variable. In fact, you may be surprised to know that Python's type hint system is much more expressive and powerful than popular static typed languages like Java!
Now, python runtime itself ignores the type hints completely. You can declare a variable as int
and assign it to a str
and python will have no problem with it.
What use are the type hints then? Well, you can use other tools – called type checkers – to scan the code and verify that it is compliant to the type declarations.
One such tool is mypy.
Now, mypy
has been production ready for years. But still, the version 1.0 release is significant. To celebrate this, we are going to be doing a series of articles on the various facets of Python's type hinting feature.
Setting up mypy
The first thing we need to do is install mypy. Simply run pip install mypy
and you are good to go. pip
will download and install mypy.
Now that we have mypy setup, take a look at the code below.
a: int = 5
b: str = "hello"
c: int = "10"
Here we see the basic type hinting syntax. Just put a colon after the variable name and declare the type of the variable there.
You might have noticed that the third variable c
is declared as an int
but we assign a string to it. As we discussed earlier in the article, python has no issues with that. It completely ignores type hints and the code will run without any errors just fine.
Now lets run this code through mypy
> mypy test.py
test.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
Found 1 error in 1 file (checked 1 source file)
As we can see, mypy validates the types and raises an error on line 3, complaining of incompatible types between what was declared and what was assigned.
So this is the general workflow when using type hints. You regularly run mypy and it will use type hints in the code to catch type errors.
In fact most popular IDEs are preconfigured to use mypy if it is installed, and some like PyCharm and VSCode ship with their own type checkers as well. Even text editors like Vim and NeoVim can be integrated with mypy so that the type errors are shown in the editor while you are coding. A proper configuration makes it easy to catch and fix the errors during coding, rather than discover the mistake during runtime.
Type hints for functions
In the previous code we saw how we can add type hints for variables. Now lets take a look at functions.
def add(a:int, b:int) -> int:
return a + b
c = add(20, 3)
print(c)
Type hinting the arguments follows that for variables: add a colon followed by the type hint.
You can also declare what type the function will return. After the closing parenthesis add a ->
(thats a hyphen followed by greater that to form an arrow) followed by the return type. The final colon goes after the return type declaration.
Now mypy will complain if you try to pass anything other than int
to the add
function. Whats more, mypy has type inferencing, so it can figure out that c
is an int
even though we did not declare any type for c
. mypy is very smart! 🤓
add
function returns an int
. Using this knowledge it can infer that the type of variable c
must also be an int
.Modules and Typeshed
Due to type inferencing, when you use a function or module that has type hints, other parts of your code that do not contain type hints will get their type inferred and you can get a lot of benefits of the type hinting workflow.
For this reason, it is highly recommended to add type hints to function or module APIs. In fact, many third party modules already have type hints. mypy will catch errors when calling those functions, even if you don't use type hints in your own code.
Some third party modules are not type hinted. But don't worry. Python has a typeshed repository which contains type hints for many such modules.
Let's take an example. The code below uses the requests
module. This is one of the most popular modules for making HTTP requests. Unfortunately, the requests
does not use python's type hinting feature. There are some bugs in the code below, but mypy can't find them without the type hints.
import requests
response = requests.get('https://www.google.co.in',
headers='Accepts: text/html')
status = response.status_code
print('Status code: ' + status)
Fortunately, the community has contributed type hints for the module and those type hints are avaialable in the typeshed. To get the type declarations from the typeshed, we need to install the types-requests
package. Just pip install types-requests
and mypy is good to go.
Here is what mypy says about our code now
> mypy test.py
test.py:3: error: Argument "headers" to "get" has incompatible type "str"; expected "Optional[Mapping[str, Union[str, bytes]]]" [arg-type]
test.py:5: error: Unsupported operand types for + ("str" and "int") [operator]
Found 2 errors in 1 file (checked 1 source file)
First, it points out that the headers
argument to requests.get()
does not support a string type. It needs to be a dict
.
Furthermore, the type declaration says that the status_code
field is an int
, and using type inferencing, mypy knows that the status
variable is also an int
. mypy uses that knowledge to flag an error on the next line: You cannot use the +
operator to add a str
and int
.
This is a good example of how using a type hinted module can uncover bugs in our code, even when we don't use typing ourselves. You shoud check if the modules you use have type hints (many do!) and if not, whether typeshed provides type hints for the module.
Moral of the story: Type inference is a super powerful feature 🎉
Summary
So thats the basics of getting started with the type hinting feature in python. If you write python code of any considerable size, type hinting should 💯 be a part of your workflow. Why catch bugs in production when you can catch them in your editor?
And if you thought it is too much effort to adopt type hints, fear not! With type inferencing you can get good benefits without any needing to change any code. You can then incrementally add hints to your own code over time.
Now that we know how to use mypy, from the next article of the series we will start exploring the type system available in python.
Did you like this article?
If you liked this article, consider subscribing to this site. Subscribing is free.
Why subscribe? Here are three reasons:
- You will get every new article as an email in your inbox, so you never miss an article
- You will be able to comment on all the posts, ask questions, etc
- Once in a while, I will be posting conference talk slides, longer form articles (such as this one), and other content as subscriber-only