Python Arena
Step 3 of 5

Arithmetic operators

The arithmetic operators can be overloaded too. a + b calls a.__add__(b), and whatever that method returns is the result. These are the methods:

Operator Meaning Method
+ addition __add__(self, other)
- subtraction __sub__(self, other)
* multiplication __mul__(self, other)
/ division __truediv__(self, other)
// integer division __floordiv__(self, other)

Here's a class for durations, such as the length of a song. Adding two durations gives a new duration:

class Duration:
    def __init__(self, minutes, seconds):
        self.minutes = minutes
        self.seconds = seconds

    def __str__(self):
        return f"{self.minutes} min {self.seconds} s"

    def total_seconds(self):
        return self.minutes * 60 + self.seconds

    def __add__(self, other):
        total = self.total_seconds() + other.total_seconds()
        return Duration(total // 60, total % 60)


intro = Duration(3, 40)
solo = Duration(4, 35)
both = intro + solo
print(both)
print(intro)
print(solo)
Output
8 min 15 s
3 min 40 s
4 min 35 s

__add__ returns a new object and leaves both of the originals as they were. That's what + does with numbers too: 2 + 3 doesn't change the 2. If __add__ changed self instead, both = intro + solo would quietly change intro as well, and that's a surprise nobody wants.

The other operand can be something else

The two sides of an operator don't have to be of the same class. Multiplying a duration by a whole number makes sense: a song played three times. A difference that would be negative doesn't make sense, so __sub__ raises a ValueError, as a class does when it's given a value it can't accept:

class Duration:
    def __init__(self, minutes, seconds):
        self.minutes = minutes
        self.seconds = seconds

    def __str__(self):
        return f"{self.minutes} min {self.seconds} s"

    def total_seconds(self):
        return self.minutes * 60 + self.seconds

    def __sub__(self, other):
        total = self.total_seconds() - other.total_seconds()
        if total < 0:
            raise ValueError("a duration can't be negative")
        return Duration(total // 60, total % 60)

    def __mul__(self, times):
        total = self.total_seconds() * times
        return Duration(total // 60, total % 60)


intro = Duration(3, 40)
solo = Duration(4, 35)
print(intro * 3)
print(solo - intro)
print(intro - solo)
Output
11 min 0 s
0 min 55 s
Python stops with
Traceback (most recent call last):
  File "main.py", line 27, in <module>
    print(intro - solo)
          ~~~~~~^~~~~~
  File "main.py", line 15, in __sub__
    raise ValueError("a duration can't be negative")
ValueError: a duration can't be negative

In intro * 3, self is intro and times is 3.

+= comes for free

a += b is short for a = a + b, so it works as soon as __add__ does. It makes a new object and puts it in the variable. That's handy for a total:

total = Duration(0, 0)
for song in album:
    total += song

As with comparisons, write only the operators that mean something for your class. Adding two durations makes sense; dividing a duration by another duration, maybe not.

Your task

Challenge 1 of 3

The class Weight can already add weights. Fill in the blank with the name of the method that makes - work, so the program prints:

700 g
300 g
500 g

Challenge 2

A vector is a step on a grid: x steps right and y steps up. Add three methods to the class Vector. Each returns a new vector and leaves the original ones as they are:

  • + adds two vectors: the new x is the sum of the two x values, and the same for y.
  • - subtracts the other vector the same way.
  • * multiplies a vector by a whole number: both x and y are multiplied by it.

For example,

a = Vector(2, 3)
b = Vector(1, -1)
print(a + b)
print(a - b)
print(a * 3)
print(a)

should print:

(3, 2)
(1, 4)
(6, 9)
(2, 3)

Challenge 3

The class Clock is a time of day. Add two operators to it:

  • clock + minutes returns a new clock that many minutes later. After 23:59 comes 0:00 again.
  • clock - other returns a whole number: how many minutes it takes to get from the time other to this clock's time, going forward, past midnight if needed.

For example,

start = Clock(22, 30)
end = start + 150
print(end)
print(start)
print(end - start)
print(Clock(8, 15) - Clock(7, 50))

should print:

1:00
22:30
150
25