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)
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)
11 min 0 s
0 min 55 sTraceback (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 negativeIn 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.