mypy 列表中对象继承的问题
mypy trouble with inheritance of objects in lists
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' 的错误 'Incompatible types in assignment '。
Dog 是继承自 Animal 的 class。
例如,我没有变量 'arr2' 的错误。
想象一下这是可能的:
arr3: List[Animal] = arr1
现在你认为你有动物列表,但这实际上是狗的列表(注意 arr3
不是 arr1
的副本,它们 相同 列表)。
并且因为您认为这是动物列表,所以您可以向其中添加 Cat
。
但是,因为这实际上是狗的列表,所以您不能向其中添加 Cat
。否则,在尝试使用特定于狗的属性后,您将在 AttributeError
上失败。
更一般地说,列表是不变的 - List[Animal]
不能分配给 List[Dog]
(因为它已经可以包含猫)并且 List[Dog]
不能分配给 List[Animal]
(因为你可以稍后添加 cat)
这在Python中可能不是很明显,但你可以做简单的测试:
arr3: List[Animal] = arr1
arr3.append(Cat())
for dog in arr1:
print(dog.bark())
Mypy 不允许这样做,因为这种分配可能会破坏您的代码逻辑
您可以尝试使用 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 =)
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' 的错误 'Incompatible types in assignment '。 Dog 是继承自 Animal 的 class。 例如,我没有变量 'arr2' 的错误。
想象一下这是可能的:
arr3: List[Animal] = arr1
现在你认为你有动物列表,但这实际上是狗的列表(注意 arr3
不是 arr1
的副本,它们 相同 列表)。
并且因为您认为这是动物列表,所以您可以向其中添加 Cat
。
但是,因为这实际上是狗的列表,所以您不能向其中添加 Cat
。否则,在尝试使用特定于狗的属性后,您将在 AttributeError
上失败。
更一般地说,列表是不变的 - List[Animal]
不能分配给 List[Dog]
(因为它已经可以包含猫)并且 List[Dog]
不能分配给 List[Animal]
(因为你可以稍后添加 cat)
这在Python中可能不是很明显,但你可以做简单的测试:
arr3: List[Animal] = arr1
arr3.append(Cat())
for dog in arr1:
print(dog.bark())
Mypy 不允许这样做,因为这种分配可能会破坏您的代码逻辑
您可以尝试使用 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 =)