Not exactly; following the order of operations, v1.__add__(v2)
is called first, and then .__add__(v3)
is called on the resulting Vector
. You can easily demonstrate this yourself by inserting a print()
call in __add__
, like this:
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return f"Vector({self.a}, {self.b})"
def __add__(self, other):
result = Vector(self.a + other.a, self.b + other.b)
print(f"Calling __add__ from {self} on {other}, result {result}")
return result
(Note that I’ve used modern f-string formatting to simplify your __str__
call.)
Calling this, we can indeed see that the result matches what is stated above:
>>> Vector(1, 2) + Vector(3, 4) + Vector(5, 6)
Calling __add__ from Vector(1, 2) on Vector(3, 4), result Vector(4, 6)
Calling __add__ from Vector(4, 6) on Vector(5, 6), result Vector(9, 12)
Vector(9, 12)
Note: You probably want to raise an appropriate TypeError
if other
is not a Vector
instance, like other __add__
operations do with incompatible types. This produces a much clearer error for users, ensures the error class matches regardless of the other of additions, and avoids accidentally concatenating incompatible types in case one happens to have a a
and a b
attribute. Something like:
def __add__(self, other):
if not isinstance(other, type(self)):
raise TypeError(f"Can only concatenate {type(self)} (not {type(other)}) to {type(self)}")
result = Vector(self.a + other.a, self.b + other.b)
print(f"Calling __add__ from {self} on {other}, result {result}")
return result