mypy在列表中继承对象时出现问题

时间:2018-05-12 11:18:43

标签: python python-3.x typing mypy

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'中出现错误'赋值不兼容'。 狗是一种继承自动物的类。 例如,我没有变量'arr2'的错误。

2 个答案:

答案 0 :(得分:4)

想象一下,这是可能的:

arr3: List[Animal] = arr1

现在您认为自己有动物列表,但这实际上是一个狗列表(请注意arr3不是arr1的副本,它们相同列表)。

因为您认为这是动物列表,您可以添加Cat

但是,因为这实际上是狗的列表,所以你不能添加Cat。否则,在尝试使用特定于狗的属性后,您将在AttributeError失败。

更一般地说,列表是不变的 - List[Animal]无法分配到List[Dog](因为它已经包含猫)而且List[Dog]无法分配给List[Animal](因为你以后可以添加猫)

这在Python中可能并不明显,但您可以进行简单的测试:

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

Mypy不允许这样做,因为此分配可能会破坏您的代码逻辑

答案 1 :(得分:2)

您可以尝试使用 Sequence[Animal]which is covariant

List[T] 是不变的;它只会处理类型为 T 的项目。这意味着 List[Dog] 不是 List[Animal] 的子类型。这是因为@awesoon 提到的,它可以防止您意外添加与 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) 

另一方面,Sequence[T] 与 T 是协变的,这意味着 Sequence[Dogs] Sequence[Animals] 的子类型。这是允许的,因为 Sequence 没有“插入”方法,因此您永远不会意外地将 Cat 潜入 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 =)