1

I am making a simple vector class, and I'm struggling to understand how mypy is working on my __add__ and __sub__ methods (in particular the difference in the mypy output in Code 1 vs Code 3 below).

Code 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)

Code 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)

Code 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)

With Code 1, I get the following error when running mypy:

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

With Code 2 and Code 3 I get no errors.

Why do I get an error with Code 1 and not Code 2?

Second (and this confuses me more), why do I not get the same error from Code 3 as I do with Code 1?

Many thanks.

EDIT

I guess the answer is because the superclass NamedTuple allows for addition, but specifies that the second argument is type object which Code 2 allows for, and Code 1 doesn't.

And that mypy is find with Code 3 because NamedTuple doesn't implement __sub__.

Frank
  • 223
  • 3
  • 11

1 Answers1

4

The Liskov substitution principle (LSP) states that an object of a subtype should always be useable in place of an object of the supertype. In this case, the supertype is tuple, which has an __add__ method accepting another tuple.

In your Code #1, you override this __add__ method but your overridden method cannot be used in place of the original one, because it only accepts a Vector, not an arbitrary tuple. So this violates the LSP, and hence it is a type error.

Your Code #2 has no error because your overridden __add__ method accepts any object, so any valid argument for the original method is also a valid argument for the new method. This means the subtype can be used in place of the supertype, so there is no violation of the LSP.

Your Code #3 has no error because your __sub__ method does not override any method from the supertype - subtraction of a tuple is not defined - and adding a new method doesn't violate the LSP because when the object is used in place of the supertype, this new method simply wouldn't be called.

kaya3
  • 47,440
  • 4
  • 68
  • 97