Improve your Python projects with mypy

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

For Developers Using Software

5 Comments

  1. KM

    Hey, in the example code there is a missing space between def and init

  2. 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.

  3. cz

    nice

  4. Thanks for this great article, I have shared it on Facebook.

Comments are Closed

The opinions expressed on this website are those of each author, not of the author's employer or of Red Hat. Fedora Magazine aspires to publish all content under a Creative Commons license but may not be able to do so in all cases. You are responsible for ensuring that you have the necessary permission to reuse any work on this site. The Fedora logo is a trademark of Red Hat, Inc. Terms and Conditions