我正在尝试在 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
只有 Dog
s。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 试图警告我的内容:以下代码段在语法上是有效的,但可能会导致问题,因为 DogHouse 中突然可能有另一种动物(猫、袋鼠、...)(正式地,代码可能违反Liskov替换原则):
doghouse = DogHouse([])
doghouse._items.append(Cat())
但是,我的代码应该解决这个问题,例如通过检查 DogHouse.add()
中的类型, 制作 Zoo._items
(有点)私有(private)的,并丰富copy()
的可变序列,所以 Zoo._items
无法修改。有没有办法同时制作
DogHouse
Zoo
的子类(并受益于 Zoo
中的通用方法),以及使用类型提示来验证我的代码不会意外允许猫或其他动物潜入狗屋?我已阅读 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.
请参阅已接受的答案以获取正确答案,并解释为什么这是错误的。
最佳答案
您在之前的编辑中使用协变类型变量所做的尝试很接近,但类型变量不应该是协变的。使其成为协变意味着 Zoo[Dog]
也是 Zoo[Animal]
,特别是,这意味着 add(self, animal: Animal_co)
无论如何都可以带走任何动物Animal_co
势必会。您正在寻找的行为确实是不变的,而不是协变的。 (您可能需要一个单独的“只读”动物园 ABC 或实际上是协变的协议(protocol)。)
当你在做的时候,不要再戳 parent 的实现细节了:
T = TypeVar('T', bound=Animal)
class Zoo(Generic[T]):
_items: List[T]
def __init__(self, items: Iterable[T]):
self._items = list(items)
def add(self, animal: T) -> None:
self._items.append(animal)
def animal_count(self) -> int:
return len(self._items)
def get_animals(self) -> List[T]:
return self._items.copy()
class DogHouse(Zoo[Dog]):
def add(self, dog: Dog) -> None:
assert isinstance(dog, Dog)
super().add(dog)
assert
是否在运行时进行类型检查。如果您实际上并不关心运行时强制,您可以简化 DogHouse
到class DogHouse(Zoo[Dog]): pass
或将其完全删除并使用 Zoo[Dog]
直接地。
关于python - 如何在 Python 中为协变可变集合类类使用类型提示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63089905/