Python Type Annotations
Type hints/annotations for Python are introduced in PEP 484 and PEP 526. The python interpreter completely ignores type hints/annotations, it is type syntax which is formalized so that third-party tools such as IDEs or type checkers such as MyPy can then use it to provide type checking and other useful type inference capabilities.
The JetBrains range of IDEs support these type hints (e.g. PyCharm, IntelliJ IDEA).
Basic Types
Type annotations are specified for variables by adding a :
and then a type after the variable name. The type for variables returned from a function are specified by adding a ->
and then the type after the input parameter list of the function, but before the :
which delimits the end of the function definition.
There is also None
, which is commonly used to define the return type of a function that doesn’t return anything.
Lists, Dicts, e.t.c
Although for basic types you do not have to import anything, for the more advanced types it is recommended you use the types defined in the typing
module, which is included in the basic Python installation.
Optional
When a variable could be either of a particular type or None
, use the Optional[<Type>]
type.
Optional[<Type>]
is the same as Union[Type, None]
but is easier to type, and expresses the design intent of the code in a clearer fashion.
If you want to ignore any type checking (this works with mypy):
Generators
Python functions that use the yield
keyword return a special type of object called a generator. You can use the Generator[]
type annotation for the return type of generator functions.
The generator type has the syntax: Generator[yield_type, send_type, return_type]
. If you don’t send anything to the generator, no return anything with the return
statement, this simplifies to Generator[yield_type, None, None]
:
You can also specify the return type as Iterable[yield_type]
:
mypy
mypy is a third-party static type checker for Python, which utilizes the official Python type annotation specification described above (which applied to Python 3.6 or greater, but you can use mypy on lower versions of Python using a different, non-official type syntax).
mypy allows you to slowly introduce types into an existing code base without having to convert everything at once.
You can install mypy with:
You can check a Python program with the command:
Ignoring Code
You can tell mypy to ignore specific lines of code with # type: ignore
:
The mypy.ini File
mypy can read a project level mypy.ini
file which you can use to configure mypy. You can use this configuration file to enable/disable certain type checking features, to prevent certain files/directories from being type checked, and more.
The mypy Daemon (dmypy)
The mypy daemon (controlled with the executable dmypy
), is a background server process which caches program state, making mypy run much faster on successive runs (e.g. rather than mypy taking an agonizing 30s to run, it runs in <1s). The mypy daemon is installed along with mypy.
You can run the mypy daemon with (assuming your working directory is the root directory you want to check in):
It is helpful to pipe the output to less
so that you can clear the output once you have addressed the issues.
If you have a free terminal window, you can even incorporate watch
so the errors update as you implement fixes:
mypy And Reusing Variables With Different Types
mypy does not like it when you re-use a variable for a different type:
One solution is to declare the variable as type Any
the first time you use it:
However, this could be considered a bad coding practice as you are loosing all the advantages of type checking when casting to Any
. Although it adds more code, a better alternative is sometimes to create separate variables:
Hopefully you can come up with more descriptive names than just key_1
and key_2
.