mypy:如何将返回值定义为子类实例列表

问题描述 投票:0回答:1

我试图将方法

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 类型系统,所以我可能会丢失一些东西。

python python-typing mypy
1个回答
4
投票

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

 章节,特别是 
泛型类型的变体 部分。

© www.soinside.com 2019 - 2024. All rights reserved.