The mypy utility is a static type checker for Python. It combines the benefits of dynamic typing and static typing. As you may know, the Python programming language is dynamically typed. So what does static type checking for Python mean? Read below to find out.
What is a type?
To store data used by a program, the system needs to know how much memory space is needed. To determine that, programming languages use types. A type matches a size of memory that must be allocated for the program to store the data. Some of the most common types are integer, float, and string.
A dynamically typed language checks types in the program during run time. Python has a “weak” type system, meaning the interpreter doesn’t enforce type checking.
A statically typed language checks types based on its analysis of the source code before the program runs. When a program passes a static type check, it is guaranteed to satisfy some set of type safety. Static type checking detects possible errors in the application code before run time. This is why mypy, which statically type checks Python applications, is useful.
Installation and running mypy
Since mypy is packaged in Fedora, installation is easy:
$ dnf install python3-mypy
Now, create a simple Python application to test and understand how mypy works.
class Person(): def __init__(self,surname,firstname,age,job): self.surname = surname self.firstname = firstname self.age = age self.job = job def display_doctors(persons): for person in persons: if person.job.lower()in['gp','dentist','cardiologist']: print(f'{person.surname} {person.name}') mike = Person('Davis', 'Mike', '45', 'dentist') john = Person('Roberts', 'John', 21, 'teacher') lee = Person('Willams', 'Lee', 'gp', 56) display_doctors(mike) display_doctors([mike, john, 'lee'])
Save this code snippet into a file named testing_mypy.py. Next, run mypy against the test code:
$ mypy testing_mypy.py
Note mypy is permissive by default. When you run against the example no error is returned. This default is useful for an app with a large code base, since you can gradually introduce mypy into your project.
Adding type hints
Using the default Python 3.6 in Fedora 27 and up, you can use type hints to annotate your code. Then mypy can check the application against these type hints. Next, use an editor to add type hints to the display_doctors function in the example program:
from typing import List def display_doctors(persons: List[Person]) -> None: for person in persons: if person.job.lower()in['gp','dentist','cardiologist']: print(f'{person.surname} {person.name}')
The example adds the following hints:
- List[Person] – This syntax expresses that the display_doctors function expects a list of Person object as an argument.
- -> None – This syntax specifies that the function will return a None value.
Now, run mypy again:
$ mypy testing_mypy.py test_mypy.py:19: error: "Person" has no attribute "name" test_mypy.py:26: error: Argument 1 to "display_doctors" has incompatible type "Person"; expected "List[Person]" test_mypy.py:27: error: List item 2 has incompatible type "str"; expected "Person"
This results in some errors, which you can fix as follows:
- First, in the print statement the program tries to access person.name, which does not exist. Instead, the program should use person.firstname.
- The second error occurs when the program calls display_doctors for the first time. The expected argument is a list of Person, but the example only passes a Person.
- Finally, the last error is due to a mistake in the list of Person. Instead of adding the Person object lee to the list, the example app has added the string ‘lee’.
Here are the relevant fixes for the example program:
def display_doctors(persons: List[Person]) -> None: for person in persons: if person.job.lower()in['gp','dentist','cardiologist']: print(f'{person.surname} {person.firstname}') ...
display_doctors([mike]) display_doctors([mike, john, lee])
Edit the code as above, and run mypy again.
There are other errors in the program. Your exercise as a reader is to find them. Look at using type hints for the Person class. Try it by yourself first. If you want to check your work, the error free program is available on Github.
Conclusion
Hopefully this short introduction to mypy shows you some benefits for your code base. mypy makes it easier to maintain your code, and to read the application code. You can also catch typos and mistakes before your code runs. Note that mypy is also available for Python 2.7 applications. However, it uses a different syntax based on commenting.
Photo by Chris Ried on Unsplash
KM
Hey, in the example code there is a missing space between def and init
Clément Verna
Thanks for spotting it. It is updated now.
Joe Tennies
I think I understand what you mean by dynamic languages having a “weak” type system, but I would like to point out that the word weak describes an entirely different feature of programing language type systems. A weak-typed language will automatically convert types on your behalf.
There’s the static vs. dynamic and strong vs. weak being 2 separate axes. The classic example of a weak typed language is PHP where one can compare the integer 2 with the string “2” or even “2 ducks” and the equality comparision will pass (== not ===). That’s blatent, but even C will convert ints to other int types and floats without a cast (much like Python). In the case of C++, it can actually do some of the things like PHP if the constructor isn’t declared with the “explicit” keyword.
cz
nice
Software Engineering
Thanks for this great article, I have shared it on Facebook.