I'm struggling to understand when to use TypeVar and when to use Union despite spending a significant amount of time in the typing documentation, the mypy documentation, and PEP 483.
Simple version of the question: what's the difference between Numeric = TypeVar('Numeric', int, float)
and Numeric = Union[int, float]
?
Here's a more detailed example of what I'm running into:
"""example1.py
Use of Union to define "Numeric" type.
"""
from typing import Union, Sequence
Numeric = Union[int, float]
Vector = Sequence[Numeric]
Matrix = Sequence[Vector]
Check with mypy:
$ mypy --strict example1.py
Success: no issues found in 1 source file
Use TypeVar
instead:
"""example2.py
Use of TypeVar to define "Numeric" type.
"""
from typing import TypeVar, Sequence
Numeric = TypeVar('Numeric', int, float)
Vector = Sequence[Numeric]
Matrix = Sequence[Vector]
Check with mypy:
$ mypy --strict example2.py
example2.py:11: error: Missing type parameters for generic type "Vector"
Found 1 error in 1 file (checked 1 source file)
The above mypy
error is referring to the definition of Matrix
. Why is mypy
happy with example1.py
but not example2.py
?
I'm able to eliminate the error in example2.py
by changing the last line to Matrix = Sequence[Vector[Numeric]]
.
Version information:
$ python --version
Python 3.8.4
$ mypy --version
mypy 0.782
TypeVar
is for creating generic types.
Numeric = TypeVar('Numeric', int, float)
Vector = Sequence[Numeric]
... means Vector[T]
is an alias for Sequence[T]
with the constraint that issubclass(T, (int, float))
. This means mypy considers Vector
to be an incomplete type, it asks: "a Vector of what?", and you can write Vector[Numeric]
to mean: "a Vector of any Numeric".
It's often used for functions or classes, like this:
T = TypeVar('T')
V = TypeVar('V')
def map(f: Callable[[T], V], it: Iterable[T]) -> Iterator[V]:
for x in it:
yield f(x)
This means you can make things type-safe even if you don't know exactly what types you're gonna get.