If I run this code through mypy:
if __name__ == '__main__':
list1: list[str] = []
list2: list[str | int] = []
list2 = list1
It gives me following error:
error: Incompatible types in assignment (expression has type "List[str]", variable has type "List[Union[str, int]]")
Why? Isn't a list[str]
a subset of list[str | int]
? If yes, then why can't I assign list1
which doesn't have wider range of possible types to list2
?
Generic types can be classified as covariant, contravariant, or invariant. When u
is a subtype of v
, a generic type g
is
g[u]
is a subtype of g[v]
,g[v]
is a subtype of g[u]
, org[u]
nor g[v]
is a subtype of the other.list
is invariant because lists are mutable, in which case it is not safe to substitute one concrete type of list for another. Immutable sequences are covariant: Sequence[str]
is, indeed, a subtype of Sequence[str | int]
, so you could do the following:
from collections.abc import Sequence
if __name__ == '__main__':
list1: Sequence[str] = []
list2: Sequence[str | int] = []
list2 = list1
The only thing you can do with a Sequence
, regardless of whether it is a list, tuple, etc, is read from it, and anything reading from list2
will know it can't assume that any given element is a str
or an int
(though it will be one or the other, rather than, say, a float
), so it's safe to provide a sequence of either type.