MyPy type check quick start
The benefits of Python static type checking and examples have been discussed at length and widely adopted and funded by major tech companies, especially Dropbox. Python static type checking enhances code quality now and in the future by defining (constraining) variables and functions (methods).
Type enforcement can be done with assert
.
Type hinting is more concise, flexible and readable than assert
, with significantly less performance impact.
Type hinting is being continually enhanced in CPython, numerous IDEs and type annotation checkers.
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.
MyPy is installed and upgraded by:
pip install -U mypy
MyPy static type checker considers the following to be interchangeable (valid) due to duck typing:
int
↔float
float
↔complex
Note that str
is not equivalent to bytes
.
Usage
Add to pyproject.toml:
[tool.mypy]
files = ["src"]
assuming Python package files are under “src/” Then issue command:
python -m mypy
Note this command checks the package and not the top-level scripts, which must be manually specified. Configure pyproject.toml to eliminate nuisance errors or otherwise configure mypy.
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 __future__ import annotations
from pathlib import Path
def reader(fn: 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 __future__ import annotations
def reader(fn: 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 __future__ import annotations
def reader(fn: 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, or possibly some types are not yet supported for type hinting, simply use typing.Any e.g.
dict[str, typing.Any]
The default where no type is declared is typing.Any
, which basically means “don’t check this variable at this location in the code”.
As in C++, Python can type hint that a function must not return.
def hello() -> typing.NoReturn:
print("hello")
error: Implicit return in function which does not return
This is used for functions that always raise an error or always exit, and in general to help ensure control flow is not returned.