Reputation: 383
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'.
Upvotes: 11
Views: 5323
Reputation: 1396
You can try using Sequence[Animal]
, which is covariant.
List[T]
is invariant; it will only handle items of exactly type T
. This means List[Dog]
is not a subtype of List[Animal]
. This is because of what @awesoon mentioned, which is that it prevents you from accidentally adding items which are incompatible with T:
# this won't compile:
dogs : List[Dog] = [dog1, dog2]
animals : List[Animal] = dogs # compiler error: List is invariant
# if the compiler allowed the previous line,
# then `dogs` would be [dog1, dog2, cat] after the next line
animals.push(cat1)
On the other hand, Sequence[T]
is covariant with T, which means that a Sequence[Dogs]
is a subtype of Sequence[Animals]
. This is allowed because a Sequence
does not have "insert" methods, so you can never accidentally sneak a Cat
in a Sequence[Dog]
:
dogs : List[Dog] = [dog1, dog2]
animals: Sequence[Animals] = dogs # this is fair game for the compiler
animals.push(cat1) # compiler error: Sequence has no method push
# since Sequences can't add new items, you can't
# accidentally put a cat inside a list of dogs =)
Upvotes: 6
Reputation: 33691
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
Upvotes: 10