【发布时间】:2020-07-26 07:29:04
【问题描述】:
我正在尝试在 Python 中创建一个协变类集合类,如下所示:
from typing import Generic, TypeVar, List, cast
class Animal():
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
class Zoo():
def __init__(self, items: List[Animal]):
self._items = items.copy() # type: List[Animal]
def add(self, animal: Animal) -> None:
self._items.append(animal)
def animal_count(self) -> int:
return len(self._items)
def get_animals(self) -> List[Animal]:
return self._items.copy()
class DogHouse(Zoo):
def __init__(self, items: List[Dog]):
self._items = items.copy() # type: List[Dog]
def add(self, dog: Dog) -> None:
assert isinstance(dog, Dog)
self._items.append(dog)
简而言之,我喜欢继承Zoo,这样它就只需要特定类型的Animal。在这种情况下,DogHouse 仅具有 Dogs。
Mypy 在这段代码中给出了两个错误:
error: Incompatible types in assignment (expression has type "List[Dog]", base class "Zoo" defined the type as "List[Animal]")error: Argument 1 of "add" is incompatible with supertype "Zoo"; supertype defines the argument type as "Animal"
我了解 mypy 试图警告我的内容:以下 sn-p 在语法上是有效的,但可能会导致问题,因为 DogHouse 中可能突然出现另一种动物(猫、袋鼠……)(正式地,该代码可能违反了 Liskov 替换原则):
doghouse = DogHouse([])
doghouse._items.append(Cat())
但是,我的代码应该解决这个问题,例如通过检查DogHouse.add() 中的类型,将Zoo._items(在某种程度上)设为私有,并使可变序列的大量copy(),所以Zoo._items无法修改。
有没有办法让DogHouse 成为Zoo 的子类(并从Zoo 中的泛型方法中受益),以及使用类型提示来验证我的代码不会意外允许 Cats 或其他动物潜入狗屋?
我已阅读 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generics,但在将这些建议应用到我的代码时遇到了麻烦(来自像 Python 这样的鸭式语言,我对协方差的概念还不是很冗长)。
编辑:我通过定义Animal_co = TypeVar('Animal_co', bound=Animal, covariant=True) 尝试了一个解决方案,但这导致error: Cannot use a covariant type variable as a parameter. 正确答案请参阅接受的答案,并解释为什么这是错误的。
【问题讨论】:
标签: python covariance type-hinting