Not exactly sure how this vector calculating class works

Hi, here is the program:

class Vector:
    def __init__(self, a, b):
        self.a=a
        self.b=b
    def __str__(self):
        return 'Vector (%d, %d)' % (self.a, self.b)
    def __add__(self, other):
        return Vector (self.a + other.a, self.b + other.b)

    
v1= Vector(2,10)
v2= Vector(5,-2)
print(v1+v2)

I can see how this will produce the output Vector (7,8) but the I’m not sure the role “other” plays in this exactly… Can someone please clarify? Thanks!

Dave

The v1+v2 turns into v1.__add__(v2)

This is the pattern for operator overloading in python.

Maybe these docs help 3. Data model — Python 3.11.2 documentation and operator — Standard operators as functions — Python 3.11.2 documentation

well, when you add two things, you need two things to add :slight_smile:

when you write:
v1 + v2
Python calls the __add__ method of v1, and passes the value of v2 to it, so it’s the same as:

v1.__add__(v2)`

Looking at your __add__ method:

    def __add__(self, other):
        return Vector (self.a + other.a, self.b + other.b)

self in this case will be v1 == otherwill be v2. THe code then adds self to other – adding v1 to v2.

If the whole self thing is confusing – take another look at how classes work in Python – it’s pretty central.

1 Like

Thanks, sorry for the late reply. So if I define a set for v3 and type print (v1+v2+v3) v1 is self and v2 and v3 are both “others”?

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
1 Like

No. If you do v1 + v2 + v3 that become two calls to the __add__ method:

  • v1 becomes self and v2 is other in the first call;
  • the result of that, a new vector, becomes self and v3 is other in the second.
2 Likes