A blog about C#, Python, Azure and full stack development

Type Checking in Python with mypy

Python, a dynamically typed language, has been embraced by developers worldwide for its flexibility and ease of use. However, as projects grow in size and complexity, the lack of static type checking can lead to runtime errors that are hard to trace. Enter mypy, an optional static type checker for Python that bridges the gap between dynamic and static typing. By leveraging mypy, developers can enjoy the benefits of both worlds, ensuring code robustness while maintaining Python’s inherent flexibility.

Key Takeaways:

  • mypy is an optional static type checker for Python.
  • It combines the benefits of dynamic (or “duck”) typing and static typing.
  • Using mypy can help catch type-related errors during development, reducing runtime errors.

Introduction to mypy

mypy is a powerful tool that introduces optional static typing to Python. Originating as a project to explore the possibilities of adding static typing to Python, it has since matured into a widely-used tool in the Python community. By providing type annotations, developers can instruct mypy to check the types of variables, function arguments, and return values, ensuring type consistency throughout the codebase. Learn more about mypy’s background and history here.

Why Use mypy?

Benefits of Static Typing

While Python’s dynamic typing is one of its strengths, it can also be a source of bugs, especially in large codebases. Static typing, on the other hand, offers several advantages:

  1. Early Error Detection: Catch type-related errors during development rather than at runtime.
  2. Improved Code Readability: Type annotations make it clear what type of data a function expects and returns.
  3. Optimized Performance: Some compilers can optimize statically-typed code better than dynamically-typed code.

Bridging the Gap

mypy doesn’t force developers to choose between static and dynamic typing. Instead, it offers a middle ground. By using mypy, developers can gradually introduce type annotations where they see fit, allowing for a smooth transition without compromising the dynamic nature of Python.

Setting Up mypy

Getting started with mypy is straightforward. First, you need to install it:

pip install mypy

Once installed, you can run mypy on your Python scripts:


By default, mypy will only check functions and variables with type annotations. To enable stricter checking, you can use various command-line options, such as --strict-optional, which enforces strict checking of optional types.

Check out the latest features and updates on mypy here.

Basic Type Annotations with mypy

Type annotations in Python are introduced using the typing module. Here’s a simple example:

from typing import List 

def add_numbers(numbers: List[int]) -> int: 
  return sum(numbers)

In the above code, the add_numbers function expects a list of integers (List[int]) and returns an integer (int). Running mypy on this code will ensure that the function is always called with a list of integers and that it always returns an integer.

Advanced Features of mypy

mypy offers a plethora of advanced features that cater to different use cases:

  1. Generic Types: Allows for type annotations that work with multiple types.
  2. Type Aliases: Create custom names for type annotations, improving code readability.
  3. Protocols: Define custom protocols (or interfaces) that classes should implement.

By learning these features, developers can harness the full power of mypy, ensuring type safety in complex Python projects.

Real-world Use Cases

Many Python projects have adopted mypy to ensure type safety. For instance, Dropbox, one of the early adopters of mypy, integrated it into their development workflow to improve code quality and reduce bugs. The optional nature of mypy’s type checking allowed them to incrementally introduce type annotations, making the transition smooth and efficient.

mypy’s Type System

Union Types

Union types in mypy allow a variable to be one of several types. This is particularly useful in cases where a function can return values of different types. For instance:

from typing import Union 

def get_value(condition: bool) -> Union[int, str]: 
  return 42 
if condition else "Hello"

In the above example, the get_value function can return either an integer or a string, depending on the condition.

Optional Types

In Python, a variable can sometimes be None. To represent this in type annotations, mypy provides the Optional type:

from typing import Optional 

def find_user(user_id: int) -> Optional[str]: 
  # Some logic to find user return None if not user_id else "Username"

Here, the find_user function returns either a string (the username) or None.


Generics allow for type annotations that can work with multiple types, making them more flexible. For instance, you can define a function that works with lists of any type:

from typing import List, TypeVar 

T = TypeVar('T') 

def first_element(items: List[T]) -> T: 
  return items[0]

Best Practices with mypy

  1. Incremental Adoption: If you’re introducing mypy into an existing project, start with a small part of the codebase. As you become more familiar with mypy, expand its usage.
  2. Use Strict Mode: While mypy is lenient by default, using strict mode (--strict) ensures that all functions have type annotations and that there are no unchecked calls.
  3. Stay Updated: mypy is actively developed, with new features and improvements being added regularly. Stay updated with the latest releases to benefit from these enhancements.
  4. Leverage Plugins: mypy supports plugins that can help in type checking specific libraries or frameworks. Explore available plugins to enhance mypy’s capabilities.

Frequently Asked Questions (FAQs)

Q: Can I use mypy with existing Python code without type annotations?
A: Yes, you can. By default, mypy will not check functions and variables without type annotations. However, you can gradually introduce type annotations to parts of your codebase.

Q: Does mypy introduce any runtime overhead?
A: No, mypy is a static type checker. It checks your code for type consistency before runtime, ensuring there’s no runtime overhead.

Q: How does mypy handle dynamic libraries like Django or Flask?
A: mypy can handle dynamic libraries using its plugin system. There are plugins available for popular libraries like Django, which help in type checking code that uses these libraries.

With the growing complexity of Python projects, tools like mypy play a crucial role in ensuring code quality and robustness. By understanding its features and best practices, developers can harness the power of static type checking while enjoying the flexibility of Python.