There is a problem that I have come across in python that the List type is invariant – meaning that it can only hold objects of a specific type or you will get a type error (for example when running mypy) – but sometimes you need to use a collection in a more generic function which can accept all types of a base class (lets call it A) as well as all derived classes. A clear example is this
class A:
pass
class B(A):
pass
def print_list_a(my_list: List[A]) -> None:
print(my_list)
l1 = [A()]
l2 = [B()]
# print_list_a(l1)
# print_list_a(l2) # mypy error because l2 is not a list[A] and list is invariant
What is the best way to handle this issue?
2
I have come across two solutions to this problem.
One is to define a generic type that is bound to A and the other is to use an covariant type (like Sequence) so something like this
T = TypeVar('T', bound=A)
def print_list_t(my_list: List[T]) -> None:
print(my_list)
# This is fine!
print_list_t(l1)
print_list_t(l2)
# use covariant type Sequence in place of list
def print_sequence_a(my_sequence: Sequence[A]) -> None:
print(my_sequence)
# This is fine too because the sequence type is covariant
print_sequence_a(l1)
print_sequence_a(l2)
The advantage of using a bound type is that the type List
which is more specific than Sequence. However, if you are happy that your function can accept any type of sequence it can be easier to use this covariant type.