为什么mypy在child和parentclass都满足grandparent类型定义的时候报不兼容类型?
Why does mypy report incompatible types when child and parent class both satisfy grandparent type definition?
给定以下代码:
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
class 中再次指定相同的类型)满足 mypy
:
from typing import Tuple
class Grandparent:
items: Tuple[str, ...] = ()
class Parent(Grandparent):
items: Tuple[str, ...] = ('foo',)
class Child(Parent):
items = ('foo', 'bar')
为什么我需要 re-specify 在 class 层次结构中的多个位置为 items
分配相同的类型,因为 items
在所有位置的分配都满足same/original定义?有没有办法避免这样做?
我相信这是 mypy 做出的设计选择。简而言之,你的问题的症结在于:当我们覆盖某些属性时,我们是要使用与父级相同的类型,还是使用新的覆盖类型?
Mypy 选择了前者——在许多情况下它可以说更直观。例如,如果我有以下 class 层次结构:
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")
。更广泛地说,允许细化父类型是有用的,唯一的限制是子类型在细化后仍然需要遵循Liskov替换原则。
如果规则是子定义优先于方法,那么为了保持一致性,将相同的规则应用于属性是有意义的。
至于如何解决这个问题——在你看来,我可能会继续为每个赋值添加类型注释。我不认为这是一个很大的负担。
或者,我可能会考虑将整个 class 层次结构折叠成一个 class,它接受适当的元组作为 __init__
中的参数,以尝试回避需要硬编码的东西开始。但无论您尝试做什么,这都可能不是一个可行的解决方案。
给定以下代码:
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
class 中再次指定相同的类型)满足 mypy
:
from typing import Tuple
class Grandparent:
items: Tuple[str, ...] = ()
class Parent(Grandparent):
items: Tuple[str, ...] = ('foo',)
class Child(Parent):
items = ('foo', 'bar')
为什么我需要 re-specify 在 class 层次结构中的多个位置为 items
分配相同的类型,因为 items
在所有位置的分配都满足same/original定义?有没有办法避免这样做?
我相信这是 mypy 做出的设计选择。简而言之,你的问题的症结在于:当我们覆盖某些属性时,我们是要使用与父级相同的类型,还是使用新的覆盖类型?
Mypy 选择了前者——在许多情况下它可以说更直观。例如,如果我有以下 class 层次结构:
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")
。更广泛地说,允许细化父类型是有用的,唯一的限制是子类型在细化后仍然需要遵循Liskov替换原则。
如果规则是子定义优先于方法,那么为了保持一致性,将相同的规则应用于属性是有意义的。
至于如何解决这个问题——在你看来,我可能会继续为每个赋值添加类型注释。我不认为这是一个很大的负担。
或者,我可能会考虑将整个 class 层次结构折叠成一个 class,它接受适当的元组作为 __init__
中的参数,以尝试回避需要硬编码的东西开始。但无论您尝试做什么,这都可能不是一个可行的解决方案。