Typing in [[Python]] is completely optional, since it is, by default, [[Dynamic Typing|dynamically typed]]. Since PEP 484, using type annotations and a static [[type checker]] (the evolution of `mypy`) is possible in [[Python]]. This type annotations will have several benefits, like improving text editor support on variable hinting and completion and better static analysis using the new typing system.
## Typing basics
Typing in [[Python]] is gradual, meaning that some parts of the code can include annotations and others don't. However, code that does have annotations must conform to them.
If two types (T1 and T2) are considered within a gradual type system, T1 is consistent with T2 as long as value of type T1 can be assigned to a variable of type T2. This relationship is not symmetric, nor transitive. The exception to this rule is the type `Any`: it is consistent with any type and any type is consistent with `Any`.
The `typing` module includes several objects that can add magic types: `List`, `Dict`, `Iterable` and `Tuple` can receive arguments to define it more. For example, a list of 2D points can be defined as `List[Tuple[float, float]]`.
It is important to understand that types are for the checker, while classes are for run-time. However, classes can be used as types (but some types are not classes).
## The `typing` module
A `Tuple` has a set of arguments defining which elements should contain, for example `Tuple[str, int, int]`. To represent an immutable sequence, one can write `Tuple[float, ...]`.
On the other hand, `Callable` can restrict both arguments and return types. For example, a function with two float arguments that return a string can be defined as `Callable[[float, float], str]`. In case the arguments don't need to be defined, `Callable[..., str]`.
It is also possible to recreate generics by using `TypeVar` and `Generic`, although it is usually only needed when defining new container types. For everyday use, it is usually enough to define `T = TypeVar('T')` and use it as the generic label, i.e `List[T]`.
`Union` can be useful sometimes, to define a group of accepted types. However, it can lead to too-liberal type checking. Another option is to use extra arguments in a `TypeVar` to define valid constraints: if we declare a new type, `AnyStr = TypeVar('AnyStr', str, bytes)`, it will check for any subclass of any of these types, while forcing them to be equal.