当子类和父类都满足祖父母类型定义时,为什么mypy报告不兼容类型?

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

给出以下代码:

from typing import Tuple


class Grandparent:
    items: Tuple[str, ...] = ()


class Parent(Grandparent):
    items = ('foo',)


class Child(Parent):
    items = ('foo', 'bar')

mypy报告以下错误:

error: Incompatible types in assignment (expression has type "Tuple[str, str]", base class "Parent" defined the type as "Tuple[str]")

更改这样的代码(在Parent类中再次指定相同的类型)满足mypy

from typing import Tuple


class Grandparent:
    items: Tuple[str, ...] = ()


class Parent(Grandparent):
    items: Tuple[str, ...] = ('foo',)


class Child(Parent):
    items = ('foo', 'bar')

为什么给所有位置的items分配都满足相同/原始的定义,为什么我需要在类层次结构的多个位置上为items重新指定相同的类型?有没有一种方法可以避免这样做?

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

我相信这是mypy做出的设计选择。简而言之,您的问题的症结在于:当我们覆盖某些属性时,我们要使用与父属性相同的类型,还是使用新的覆盖类型?

Mypy选择了前者-在许多情况下可以说更直观。例如,如果我具有以下类层次结构:

class Parent:
    def foo(self, p1: int) -> None: ...

class Child(Parent):
    def foo(self, p1: int, p2: str = "bar") -> None: ...

... Child.foo具有def (self: Child, p1: int, p2: str = ...) -> None的类型而不是直接继承Parent.foo的类型,即def (self: Parent, p1 : int) -> None是有意义的。

这样,所有内容仍会键入是否检查Child().foo(1, "a")。更广泛地讲,允许refine父类型是有用的,唯一的限制是子在精化后仍然需要遵循Liskov替换原则。

并且如果规则是子定义赢得方法,那么为了一致性,将相同规则应用于属性是有意义的。

以及关于如何解决此问题的方法-在您的鞋子中,我可能只是选择继续为每个作业添加类型注释。我认为负担不那么大。

或者,我可能只是考虑将整个类的层次结构折叠为一个单独的类,该类在__init__中接受适当的元组作为参数,以尝试避免以硬编码开头的需求。但这对于您尝试做的任何事情可能都不是可行的解决方案。

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