mypy:“__add__”的签名与超类型 "tuple" 不兼容 - 但与 __sub__ 一切正常

mypy: Signature of "__add__" incompatible with supertype "tuple" - but everything fine with __sub__

我正在制作一个简单的矢量 class,我正在努力了解 mypy 如何处理我的 __add____sub__ 方法(特别是 mypy 中的区别在下面的代码 1 和代码 3 中输出。

代码 1:

from typing import NamedTuple

class Vector(NamedTuple):
    x: float
    y: float

    def __add__(self, other: Vector) -> Vector:
        return Vector(self.x + other.x, self.y + other.y)

代码2:

from typing import NamedTuple

class Vector(NamedTuple):
    x: float
    y: float

    def __add__(self, other: object) -> Vector:
        if not isinstance(other, Vector):
            return NotImplemented
        return Vector(self.x + other.x, self.y + other.y)

代码 3:

from typing import NamedTuple

class Vector(NamedTuple):
    x: float
    y: float

    def __sub__(self, other: Vector) -> Vector:
        return Vector(self.x - other.x, self.y - other.y)

使用代码 1,当 运行 mypy:

时出现以下错误

error: Signature of "__add__" incompatible with supertype "tuple".

使用代码 2 和代码 3 我没有错误。

为什么我收到代码 1 而不是代码 2 的错误?

其次(这让我更加困惑),为什么我从代码 3 中得到的错误与我在代码 1 中得到的错误不同?

非常感谢。

编辑

我想答案是因为 superclass NamedTuple 允许添加,但指定第二个参数是代码 2 允许的类型 object,而代码 1 不允许't.

而且 mypy 是用代码 3 找到的,因为 NamedTuple 没有实现 __sub__

Liskov substitution principle (LSP) 声明子类型的对象应该始终可用于代替超类型的对象。在这种情况下,超类型是 tuple,它有一个 __add__ 方法接受另一个 tuple.

在您的代码 #1 中,您重写了此 __add__ 方法,但您重写的方法不能用于代替原始方法,因为它只接受 Vector,而不接受任意 tuple。所以这违反了 LSP,因此是类型错误。

您的代码 #2 没有错误,因为您覆盖的 __add__ 方法接受任何 object,因此原始方法的任何有效参数也是新方法的有效参数。这意味着可以使用子类型代替超类型,因此不会违反 LSP。

您的代码 #3 没有错误,因为您的 __sub__ 方法没有覆盖超类型的任何方法 - 未定义 tuple 的减法 - 并且添加新方法不违反LSP,因为当使用对象代替超类型时,根本不会调用这个新方法。