MyPy Python static type hinting quick start

1 minute read

The benefits of Python static type checking and examples have already been given at length. In summary, Python static type checking enhances code quality now and in the future by defining (constraining) variables and functions (methods).

assert vs. type hinting

Type enforcement can be done with assert, but type hinting is much more concise, flexible and readable than assert. With type hinting, the hint is right at the variable name (e.g. in the function declaration), while assert must occur in the code body.

Duck typing

MyPy static type checker considers the following to be interchangable (valid) due to duck typing:

  • intfloat
  • floatcomplex

Note that str is not equivalent to bytes, one of the major benefits of Python 3.

Install

pip install mypy

Usage

From the code project top level:

mypy .

Currently, major packages like Numpy are adding type hinting support. In the meantime, typically use:

mypy . --ignore-missing-imports

which turns unknown types like np.ndarray into typing.Any

It takes a little practice to understand the messages. Where multiple types are accepted, for example, str and pathlib.Path use typing.Union. See the examples below.

Examples

Many times a function argument can handle more than one type. This is handled as follows:

from typing import Union
from pathlib import Path


def reader(fn: Union[Path, str]) -> str:
    fn = Path(fn).expanduser()
    
    txt = fn.read_text()
    
    return txt

Another case is where lists or tuples are used, the types within can be checked (optionally):

from typing import Union, Tuple, List


def reader(fn: Union[Path, str]) -> Tuple[float, float]:
    fn = Path(fn).expanduser()
    
    txt: List[str] = fn.read_text().split(',')
    
    latlon = (float(txt[0]), float(txt[1]))
    
    return latlon

Or perhaps dictionaries, where optionally types within can be checked:

from typing import Union, Dict, List, Any


def reader(fn: Union[Path, str]) -> Dict[str, float]:
    fn = Path(fn).expanduser()
    
    txt: List[str] = fn.read_text().split(',')
    
    params = {'lat': float(txt[0]),
              'lon': float(txt[1])}
    
    return params

If many value types are in the dictionary, you can use Union[] or simply Any e.g.

Dict[str, Any]

The default where no type is declared is Any, which basically means “don’t check this variable at this location in the code”.

Leave a comment