Question
Answer and Explanation
In MyPy, an invariant list refers to how type checking is handled with lists and their contained types. Specifically, it means that a list of a specific type is not considered a subtype of a list of a more general type, even if the contained type is a subtype of the more general type.
Let's break this down with an example:
Suppose you have two classes, Animal
and Dog
, where Dog
is a subclass of Animal
:
class Animal:
pass
class Dog(Animal):
pass
In a type system that supports covariance, you might expect that a List[Dog]
would be a subtype of List[Animal]
, because a Dog
is a subtype of Animal
. However, in MyPy, lists are invariant. This means that List[Dog]
is not considered a subtype of List[Animal]
, nor is List[Animal]
a subtype of List[Dog]
. They are treated as distinct types.
Here's why this is important:
Type Safety: If lists were covariant, you could potentially add an Animal
that is not a Dog
to a List[Dog]
, which would violate type safety. For example:
from typing import List
def add_animal(animals: List[Animal], animal: Animal) -> None:
animals.append(animal)
dogs: List[Dog] = [Dog(), Dog()]
# If List was covariant, this would be allowed, but it's not:
# add_animal(dogs, Animal()) # This would cause a type error in MyPy
# This is correct:
add_animal(dogs, Dog())
In the above example, if List
were covariant, you could pass a List[Dog]
to a function expecting a List[Animal]
. The function could then add a generic Animal
to the list, which would not be a Dog
, leading to runtime errors if you later tried to treat all elements of the list as Dog
objects. MyPy prevents this by enforcing invariance.
Implications:
- You cannot directly pass a List[Dog]
where a List[Animal]
is expected, or vice versa.
- You need to be explicit about the types of lists you are using, especially when dealing with inheritance.
- If you need to work with a list of a more general type, you might need to create a new list or use a different data structure.
In summary, an invariant list in MyPy ensures type safety by preventing you from treating a list of a specific type as a list of a more general type, even if the contained types have an inheritance relationship. This helps catch potential type errors at compile time rather than at runtime.