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.