DMarlon Studio

Lesson 10 · 10 of 20

Special methods · Python

Make your objects print, compare, add up and measure like built-in Python ones with __repr__, __eq__, __lt__, __add__ and __len__.

Python · Special methods in DMarlon Studio: the example's code and its output in the terminal

Special methods (or *dunder* methods, for the double underscores) are the ones Python calls by itself when you use operators and built-in functions. Money stores the amount in cents, an integer, so no rounding errors creep in.

`__repr__` returns the text that represents the object. print() uses it when there is no __str__ and, above all, inside lists: that is why cart.prices shows as [19.99 EUR, 4.50 EUR, 120.00 EUR] rather than memory addresses.

`__eq__` defines ==: it compares amount and currency as a tuple. Without it, two Money(450) would be different because they are different objects. `__lt__` defines <, and that alone lets sorted(), min() and max() put them in order.

`__add__` defines + and returns a new Money. It is what makes sum(cart.prices, Money(0)) work: sum starts from the second argument (which must be a Money, not the default 0) and keeps adding with +. If the currencies differ, it raises ValueError.

In Cart, `__len__` makes len(cart) work. There are many more: __getitem__ for cart[0], __contains__ for in, __bool__ for if cart:…

Code · main.py

class Money:    def __init__(self, cents, currency="EUR"):        self.cents = cents        self.currency = currency    def __repr__(self):               # how it prints, also inside lists        return f"{self.cents / 100:.2f} {self.currency}"    def __eq__(self, other):          # ==        return (self.cents, self.currency) == (other.cents, other.currency)    def __lt__(self, other):          # < (and so sorted, min, max)        return self.cents < other.cents    def __add__(self, other):         # +        if self.currency != other.currency:            raise ValueError("cannot add different currencies")        return Money(self.cents + other.cents, self.currency)class Cart:    def __init__(self, *prices):        self.prices = list(prices)    def __len__(self):                # len(cart)        return len(self.prices)cart = Cart(Money(1999), Money(450), Money(12000))print("Items:", len(cart), cart.prices)print("Total:", sum(cart.prices, Money(0)))print("Sorted:", sorted(cart.prices))print("Equal:", Money(450) == Money(450), "| less:", Money(450) < Money(1999))try:    Money(100) + Money(100, "USD")except ValueError as error:    print("Error:", error)

Output · python3 main.py

Items: 3 [19.99 EUR, 4.50 EUR, 120.00 EUR]
Total: 144.49 EUR
Sorted: [4.50 EUR, 19.99 EUR, 120.00 EUR]
Equal: True | less: True
Error: cannot add different currencies

Key points

  • Python calls special methods for you: + calls __add__.
  • __repr__ is the representation; make it clear.
  • __eq__ defines == and __lt__ is enough for sorting.
  • __len__ makes len() work.
  • Storing money as whole cents avoids rounding errors.

Exercise

Add __mul__ to Money so that Money(1999) * 3 returns 59.97 EUR, and use it to charge three units of the first item.