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.
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.
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.
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.
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.
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.
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