mypy:如何将 return 值定义为子 class 实例的列表
mypy: How to define return value as a list of sub-class instances
我正在尝试将方法 foo
的 return 值定义为 AbstractChild
子 class 实例的列表,但 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
将 return 类型从列表更改为单个值将使 mypy 停止抱怨,我觉得这很奇怪,但我仍然习惯 python 类型系统,所以我可能缺少一些东西。
mypy
在这里是正确的,因为你的 Parent
没有正确实现 AbstractParent
- 为此,它应该定义一个方法 foo
return 是 AbstractChild
ren 的列表,而不是 Child
ren。这是因为集合不是多态的(对于其他语言也是如此,例如 Java):List[AbstractChild]
与 List[Child]
不是同一类型,而 List[Child]
不是不要仅仅因为 Child
继承了 List[AbstractChild]
。如果我们没有这个限制,像这样的错误是可能的:
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's answer for a similar question in Java 的改写示例)。
Java,例如,在编译时捕获此类错误并需要显式协方差,例如List<? extends Child>
用于读取列表,List<? super Child>
用于写入列表。
在你的例子中,你也会引入一个通用类型。在下面的示例中,我将 AbstractParent
更改为 return 具有相同类型 C
的 List
元素可以是任何子类 AbstractChild
和 Parent
是具有具体子类型 Child
:
的泛型 AbstractChild
的具体实现
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 []
有关更多示例,请查看 Generics chapter from mypy
docs, in particular the Variance of generic types 部分。
我正在尝试将方法 foo
的 return 值定义为 AbstractChild
子 class 实例的列表,但 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
将 return 类型从列表更改为单个值将使 mypy 停止抱怨,我觉得这很奇怪,但我仍然习惯 python 类型系统,所以我可能缺少一些东西。
mypy
在这里是正确的,因为你的 Parent
没有正确实现 AbstractParent
- 为此,它应该定义一个方法 foo
return 是 AbstractChild
ren 的列表,而不是 Child
ren。这是因为集合不是多态的(对于其他语言也是如此,例如 Java):List[AbstractChild]
与 List[Child]
不是同一类型,而 List[Child]
不是不要仅仅因为 Child
继承了 List[AbstractChild]
。如果我们没有这个限制,像这样的错误是可能的:
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's answer for a similar question in Java 的改写示例)。
Java,例如,在编译时捕获此类错误并需要显式协方差,例如List<? extends Child>
用于读取列表,List<? super Child>
用于写入列表。
在你的例子中,你也会引入一个通用类型。在下面的示例中,我将 AbstractParent
更改为 return 具有相同类型 C
的 List
元素可以是任何子类 AbstractChild
和 Parent
是具有具体子类型 Child
:
AbstractChild
的具体实现
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 []
有关更多示例,请查看 Generics chapter from mypy
docs, in particular the Variance of generic types 部分。