Skip to content
  • Magic (Dunder) Methods · Python

Magic (Dunder) Methods

LearnViaNotes @learnvianotes· 2 min read

Double-underscore methods let your objects plug into Python's syntax: printing, ==, len(), [], for, +, with and more.

Representation

Python
class Money:
    def __init__(self, amount, cur="INR"):
        self.amount, self.cur = amount, cur
    def __repr__(self):                  # unambiguous, for developers
        return f"Money({self.amount!r}, {self.cur!r})"
    def __str__(self):                   # readable, for users
        return f"{self.amount:.2f} {self.cur}"

m = Money(250)
print(repr(m))        # Money(250, 'INR')
print(m)              # 250.00 INR
print([m])            # [Money(250, 'INR')]

Equality, hashing & ordering

Python
from functools import total_ordering

@total_ordering
class Version:
    def __init__(self, major, minor):
        self.key = (major, minor)
    def __eq__(self, other):
        return isinstance(other, Version) and self.key == other.key
    def __hash__(self):                   # equal objects must hash equally
        return hash(self.key)
    def __lt__(self, other):
        return self.key < other.key

v1, v2 = Version(1, 2), Version(1, 10)
print(v1 < v2, v1 >= v2, v1 == Version(1, 2))   # True False True
print(len({v1, Version(1, 2)}))                  # 1

Container & iteration protocols

Python
class Playlist:
    def __init__(self, *songs):
        self._songs = list(songs)
    def __len__(self):
        return len(self._songs)
    def __getitem__(self, i):             # enables p[i], slicing and iteration
        return self._songs[i]
    def __contains__(self, song):
        return song in self._songs

p = Playlist("a", "b", "c")
print(len(p), p[0], p[-1], "b" in p)      # 3 a c True
print(list(p), p[1:])                     # ['a', 'b', 'c'] ['b', 'c']

Operators & callables

Python
class Vec:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __add__(self, o):
        return Vec(self.x + o.x, self.y + o.y)
    def __mul__(self, k):
        return Vec(self.x * k, self.y * k)
    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5
    def __repr__(self):
        return f"Vec({self.x}, {self.y})"

class Adder:
    def __init__(self, n):
        self.n = n
    def __call__(self, x):                # instances become callable
        return x + self.n

print(Vec(1, 2) + Vec(3, 4), Vec(3, 4) * 2, abs(Vec(3, 4)))   # Vec(4, 6) Vec(6, 8) 5.0
print(Adder(10)(5))                                            # 15
SyntaxCalls
str(x), repr(x)__str__, __repr__
x == y, x < y__eq__, __lt__ (+ __le__, __gt__, __ge__)
len(x), x[i], v in x__len__, __getitem__, __contains__
for v in x__iter__ (then __next__ on the iterator)
x + y, x * y__add__, __mul__ (__radd__ for y + x)
x()__call__
with x:__enter__, __exit__
bool(x)__bool__, falling back to __len__

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report