An Example of Covariance, Contravariance and Invariance in Python Type Hints
Date: 12/12/2023 · Tags: #python, #dev, #functional-programmingQiangning Hong (@hongqn) share a Python type annotation tip on X (Twitter), and raised discussions about covariance, contravariance and invariance in Python
from collections.abc import Sequence
a: list[str] =["a"]
def f1(a: list[int | str]): pass
def f2(a: Sequence[int | str]): pass
f1(a) # incompatible type assignment
f2(a) # compatible
So why?
The key point is that list[int | str]
is not covariant with list[str]
, while
Sequence[int | str]
is covariant with list[str]
.
The reason of Sequence
is immutable and is also covariant, most immutable
containers are covariant, so f2
cannot modify the list even if it is passed as
a list.
List
is mutable, so if the parameter is declared as a list type, list[str]
and list[int | str]
are two different types. In type system, mutable types
usually are invariant (e.g. MutableSequence
, list
, set
in Python).
It would be more clear that we add an function to accept Sequence
type, and it
will be compatible with variable a
:
def f3(a: Sequence[str]): pass
f3(a) # compatible
Refs: