Search code examples
pythonpython-typingmypy

List[Dog] is incompatible with List[Animal] where Dog inherits from Animal


Python 3.6.5 and mypy 0.600

I wrote the code:

from typing import List


class Animal():
    pass


class Dog(Animal):
    def __init__(self) -> None:
        super()

    def bark(self) -> None:
        pass


class Cat(Animal):
    def __init__(self) -> None:
        super()

    def meow(self) -> None:
        pass


arr1: List[Dog] = [Dog(), Dog()]
arr2: List[Animal] = [Dog(), Dog()]

# error: Incompatible types in assignment (expression has type "List[Dog]", variable has type "List[Animal]")
arr3: List[Animal] = arr1

I don't understand, why I have an error 'Incompatible types in assignment ' with a variable 'arr3'. Dog is a class which inherits from a Animal. For example, I don't have an error with variable 'arr2'.


Solution

  • Imagine that this would be possible:

    arr3: List[Animal] = arr1
    

    Now you think you have list of animals, but this is actually a list of dogs (note that arr3 is not a copy of arr1, they are the same list).

    And because you think this is the list of animals you can add a Cat to it.

    However, because this is actually list of dogs, you cannot add a Cat to it. Otherwise you will fail on AttributeError after trying to use dog-specific attribute.

    More generally, list is invariant - List[Animal] cannot be assigned to List[Dog] (because it can already contain cats) and List[Dog] cannot be assigned to List[Animal] (because you can add cat later)


    This might not be obvious in Python, but you can make simple test:

    arr3: List[Animal] = arr1 
    arr3.append(Cat())
    for dog in arr1:
        print(dog.bark())
    

    Mypy does not allow this because this assignment might break your code logic