Skip to content

Expert · Object-oriented programming · topic 38 of 50 · 30 min

Dunder (magic) methods

Explanation

Dunder methods have names with double underscores on both sides, like __str__ and __add__. Python calls them for you behind the scenes: print() calls __str__, == calls __eq__, < calls __lt__, + calls __add__, len() calls __len__ and in calls __contains__. Writing them makes your own objects work like built-in ones.

What "dunder" means

"Dunder" is short for "double underscore". __init__ is read aloud as "dunder init". These special methods are also called magic methods, because you rarely call them yourself: Python calls them when you use an operator or a built-in function on your object.

Think of it like this: Think of the standard plug on an Indian mobile charger. Any phone with the matching socket works with it. Dunder methods are the standard sockets of Python: if your object has a __len__ socket, the len() plug fits; if it has __add__, the + plug fits.
You writePython calls
print(obj) or str(obj)obj.__str__()
repr(obj), or obj shown inside a listobj.__repr__()
a == ba.__eq__(b)
a < b, and sorted(list_of_objects)a.__lt__(b)
len(obj)obj.__len__()
a + ba.__add__(b)
x in objobj.__contains__(x)

__str__ and __repr__

__str__ gives a friendly text for users, used by print(). __repr__ gives an exact text for programmers, used when debugging and when the object appears inside a list. A good __repr__ looks like the code that would create the object.

python
class Book:
    def __init__(self, title, price):
        self.title = title
        self.price = price

    def __str__(self):
        return f"{self.title} (Rs {self.price})"

    def __repr__(self):
        return f"Book({self.title!r}, {self.price})"

b = Book("Wings of Fire", 299)
print(b)          # Wings of Fire (Rs 299)
print([b])        # [Book('Wings of Fire', 299)]
Without these two methods, print(b) shows something like <__main__.Book object at 0x...>.
Tip: If you only write one, write __repr__. Python falls back to __repr__ when there is no __str__.

A Money class: __add__, __eq__ and __lt__

python
class Money:
    def __init__(self, paise):
        self.paise = paise                 # store whole paise: no float errors

    def __add__(self, other):
        if not isinstance(other, Money):
            return NotImplemented
        return Money(self.paise + other.paise)   # a NEW object

    def __eq__(self, other):
        if not isinstance(other, Money):
            return NotImplemented
        return self.paise == other.paise

    def __lt__(self, other):
        return self.paise < other.paise

    def __repr__(self):
        return f"Money({self.paise})"

    def __str__(self):
        return f"Rs {self.paise // 100}.{self.paise % 100:02d}"

tea = Money(1500)
samosa = Money(2050)
print(tea + samosa)                        # Rs 35.50
print(Money(100) == Money(100))            # True
print(sorted([samosa, tea, Money(500)]))   # smallest first
sorted() only needs __lt__ to compare two items.

Returning NotImplemented (a special built-in value) tells Python "I do not know how to handle this type". Python then tries the other object, and if that fails too, it raises a clear TypeError. This is better than crashing with a confusing AttributeError.

Careful: Without __eq__, == checks whether two names point to the SAME object, so Money(100) == Money(100) would be False. Also note: when you define __eq__, Python removes the default __hash__, so your objects can no longer be put in a set or used as dict keys unless you also write __hash__.

__len__ and __contains__

python
class Playlist:
    def __init__(self, name):
        self.name = name
        self.songs = []

    def add(self, song):
        self.songs.append(song)

    def __len__(self):
        return len(self.songs)

    def __contains__(self, song):
        return song in self.songs

p = Playlist("Road trip")
p.add("Kesariya")
p.add("Malang")
print(len(p))            # 2
print("Malang" in p)     # True
print("Tum Hi Ho" in p)  # False
Your object now works with len() and the in keyword.
Tip: The functools module has total_ordering: write __eq__ and __lt__, add @functools.total_ordering above the class, and Python fills in <=, > and >= for you.

Watch the lesson

Dunder (magic) methods · 1/7

Dunder methods

In this lesson you will learn dunder methods, the special methods that make your objects work with print, plus, equals and more.

Prefer a video by a teacher? Find free videos on “python magic methods dunder str repr eq add” on YouTube ↗

In real life: A shopping cart that adds up

An online kirana app adds up prices from many shops. Using a Money class with __add__ means cart totals are written as plain item1 + item2, money is stored in exact paise, and print() always shows a neat "Rs 35.50". Sorting items by price with sorted() works because of __lt__.

python
class Money:
    def __init__(self, paise):
        self.paise = paise
    def __add__(self, other):
        return Money(self.paise + other.paise)
    def __str__(self):
        return f"Rs {self.paise // 100}.{self.paise % 100:02d}"

cart = [Money(4500), Money(12000), Money(2999)]
total = Money(0)
for price in cart:
    total = total + price
print("Cart total:", total)
Cart total: Rs 194.99

Practice (0/3 done)

Task 1 · Code · Python

Add a __str__ method to the Book class so that print(Book("Wings of Fire", 299)) shows exactly: Wings of Fire (Rs 299)

Task 2 · Code · Python

Complete the Money class (amounts in paise). Write __add__ so Money(1500) + Money(2050) gives a new Money(3550), and __eq__ so two Money objects with the same paise are equal.

Task 3 · Code · Python

Complete the Playlist class: write __len__ to return the number of songs, and __contains__ so that "Kesariya" in playlist works.

Quick quiz

Question 1

1.Which method does print(obj) use?

Question 2

2.What does a + b call when a is your object?

Question 3

3.Without __eq__, what does Money(100) == Money(100) give?

Question 4

4.Which method does sorted() need to compare your objects?

Question 5

5.What should __add__ return when the other value is a type it cannot handle?

Read more (free)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: Inheritance and composition