mypy麻烦列表对象的继承

use*_*175 6 python typing python-3.x 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
Run Code Online (Sandbox Code Playgroud)

我不明白,为什么我有一个错误“赋值中的不兼容类型”和变量“ arr3”。狗是从动物继承的类。例如,变量'arr2'没有错误。

awe*_*oon 6

想象一下这是可能的:

arr3: List[Animal] = arr1
Run Code Online (Sandbox Code Playgroud)

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

并且因为您认为这是动物列表,所以您可以在其中添加一个Cat

但是,因为这实际上是狗的列表,所以您不能在其中添加 a 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())
Run Code Online (Sandbox Code Playgroud)

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

  • Animal -> Dog 是一个层次结构,但 `List[Animal]` -> `List[Dog]` 不是。 (3认同)
  • 不知道为什么这早先被否决了:这个答案是绝对正确的。对于 OP:如果您想了解更多详细信息,mypy 的文档有一个 [关于不变性与协方差的部分](http://mypy.readthedocs.io/en/latest/common_issues.html#invariance-vs-covariance)。 (2认同)

use*_*730 6

您可以尝试使用Sequence[Animal]它是协变的

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) 
Run Code Online (Sandbox Code Playgroud)

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