我试图将方法
foo
的返回值定义为 AbstractChild
子类实例的列表,但 mypy 一直给我一个错误。
class AbstractParent(ABC):
@abstractmethod
def foo(self) -> List["AbstractChild"]: pass
class AbstractChild(ABC):
pass
class Parent(AbstractParent):
def foo(self) -> List["Child"]: pass
# ^ mypy: error Return type "List[Child]" of "foo" incompatible with return type "List[AbstractChild]" in supertype "AbstractParent"
class Child(AbstractChild):
pass
将返回类型从列表更改为单个值将使 mypy 停止抱怨,我觉得这很奇怪,但我仍在习惯 python 类型系统,所以我可能会丢失一些东西。
mypy
在这里是正确的,因为你的Parent
没有正确实现AbstractParent
- 为此,它应该定义一个方法foo
,返回AbstractChild
ren列表,而不是Child
ren。这是因为集合不是多态的(对于其他语言也是如此,例如 Java):List[AbstractChild]
与 List[Child]
不是同一类型,并且 List[Child]
不继承自 List[AbstractChild]
,只是因为 Child
确实如此。如果我们没有这个限制,就可能会出现这样的错误:
class AbstractChild(ABC):
pass
class Child(AbstractChild):
pass
class GrandChild(AbstractChild):
pass
grandchildren: List[GrandChild] = [GrandChild()]
all_children: List[AbstractChild] = grandchildren
all_children.append(Child())
grandchild: GrandChild = grandchildren[0] # would pass typechecks but is a Child, actually
(这是 Jon Skeet 对 Java 中类似问题的回答的改写示例)。
例如,Java 在编译时捕获此类错误并需要显式协变,例如List<? extends Child>
用于读取列表,
List<? super Child>
用于写入列表。在您的情况下,您还可以引入泛型类型。在下面的示例中,我更改
AbstractParent
以返回具有相同类型
List
的元素的
C
,这些元素可以是
AbstractChild
的子类,并且
Parent
是通用
AbstractChild
的具体实现具体子类型
Child
:
from typing import List, TypeVar, Generic
C = TypeVar('C', bound='AbstractChild')
class AbstractParent(ABC, Generic[C]):
@abstractmethod
def foo(self) -> List[C]: pass
class Parent(AbstractParent["Child"]):
def foo(self) -> List["Child"]:
return []
有关更多示例,请查看
mypy
文档中的 Generics
章节,特别是泛型类型的变体 部分。