Python 3.6.5 和 mypy 0.600
我写了代码:
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
我不明白,为什么我的变量“arr3”出现“赋值类型不兼容”错误。 Dog 是一个继承自 Animal 的类。 例如,我的变量“arr2”没有错误。
想象一下这是可能的:
arr3: List[Animal] = arr1
现在你认为你有动物列表,但这实际上是狗列表(请注意,
arr3
不是arr1
的副本,它们是相同列表)。
因为您认为这是动物列表,所以您可以在其中添加
Cat
。
但是,因为这实际上是狗的列表,所以您不能在其中添加
Cat
。否则,在尝试使用特定于狗的属性后,您将会失败。更一般地,列表是不变的 -
AttributeError
不能分配给
List[Animal]
(因为它已经可以包含猫)并且 List[Dog]
不能分配给 List[Dog]
(因为你可以稍后添加猫)这在Python中可能不明显,但你可以做简单的测试:
List[Animal]
Mypy 不允许这样做,因为此分配可能会破坏您的代码逻辑
arr3: List[Animal] = arr1
arr3.append(Cat())
for dog in arr1:
print(dog.bark())
,
这是协变的。
Sequence[Animal]
是不变的;它只会处理类型为
List[T]
的项目。这意味着 T
不是
List[Dog]
的子类型。这是因为 @awesoon 提到的,它可以防止您意外添加与 T 不兼容的项目:List[Animal]
另一方面,
# 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)
与 T 是协变的,这意味着
Sequence[T]
是
Sequence[Dogs]
的子类型。这是允许的,因为 Sequence[Animals]
没有“插入”方法,因此您永远不会意外地将
Sequence
偷偷放入
Cat
中:
Sequence[Dog]