Python Dunder Methods · Examples
Object-Oriented Python

Python Dunder Methods Examples

Short, runnable programs that show python dunder (magic) methods in action. Read the explanation, press Run, then change the code and try your own ideas. New to the topic? Start with the Python Dunder (Magic) Methods lesson.

1Let the number come first with __rmul__

v * 3 calls v.__mul__(3). For 3 * v Python first asks the int, which gives up, and then tries the reflected method v.__rmul__(3). Pointing __rmul__ at __mul__ supports both orders.

Let the number come first with __rmul__
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __mul__(self, factor):
        return Vector(self.x * factor, self.y * factor)

    __rmul__ = __mul__

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v = Vector(2, 5)
print(v * 3)
print(3 * v)
Expected outputVector(6, 15)
Vector(6, 15)

2All comparisons from two methods with total_ordering

Write __eq__ and __lt__ and @total_ordering fills in <=, > and >=. The RANKS string gives each card its strength by position.

All comparisons from two methods with total_ordering
from functools import total_ordering

RANKS = "23456789TJQKA"

@total_ordering
class Card:
    def __init__(self, rank):
        self.rank = rank

    def __eq__(self, other):
        return RANKS.index(self.rank) == RANKS.index(other.rank)

    def __lt__(self, other):
        return RANKS.index(self.rank) < RANKS.index(other.rank)

    def __repr__(self):
        return f"Card({self.rank!r})"

hand = [Card("K"), Card("3"), Card("A"), Card("T")]
print(sorted(hand))
print(Card("K") >= Card("T"), Card("2") > Card("3"))
print(max(hand))
Expected output[Card('3'), Card('T'), Card('K'), Card('A')]
True False
Card('A')

3__eq__ plus __hash__ for sets and dict keys

Hashing the same fields that __eq__ compares keeps the rule that equal objects have equal hashes. Now duplicates collapse in a set and a new Point finds an existing dictionary key.

__eq__ plus __hash__ for sets and dict keys
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

    def __hash__(self):
        return hash((self.x, self.y))

visited = {Point(0, 0), Point(1, 2), Point(0, 0)}
print(len(visited))
distance = {Point(3, 4): 5}
print(distance[Point(3, 4)])
print(Point(1, 2) in visited)
Expected output2
5
True

4__contains__ and __bool__ on an inbox

in runs a custom search that ignores case and looks inside each message. __bool__ makes an empty inbox count as false, so a plain if box: reads naturally.

__contains__ and __bool__ on an inbox
class Inbox:
    def __init__(self, messages):
        self.messages = messages

    def __contains__(self, word):
        return any(word.lower() in text.lower() for text in self.messages)

    def __bool__(self):
        return len(self.messages) > 0

inbox = Inbox(["Meeting at 10", "Invoice attached"])
print("invoice" in inbox, "lunch" in inbox)
for box in [inbox, Inbox([])]:
    print("You have mail" if box else "Nothing new")
Expected outputTrue False
You have mail
Nothing new

5A callable object that remembers state

Multiplier can be passed to map() like a function, but it also keeps a call counter. A plain function cannot easily carry state around like this.

A callable object that remembers state
class Multiplier:
    def __init__(self, factor):
        self.factor = factor
        self.calls = 0

    def __call__(self, value):
        self.calls += 1
        return value * self.factor

double = Multiplier(2)
print(list(map(double, [1, 2, 3])))
print(double(10), "after", double.calls, "calls")
Expected output[2, 4, 6]
20 after 4 calls

6Built-in types use the same hooks

Nothing here is special to your classes. Integers, strings and lists implement the same dunder methods. The last number-plus-text call returns NotImplemented exactly like the code you wrote, and + turns that into a TypeError.

Built-in types use the same hooks
print((5).__add__(3), "ab".__mul__(3), [1, 2, 3].__len__())
print("cat".__contains__("a"), (7).__eq__(7), (2).__lt__(10))
print("hello".__getitem__(1))
print((5).__add__("x"))
print(5 + 3 == (5).__add__(3))
Expected output8 ababab 3
True True True
e
NotImplemented
True

Practise next

Python Dunder (Magic) Methods exercises → Back to the lesson

Python Lists examplesPython Classes and Objects examplesPython Class Methods and Static Methods examples