Magic Methods (Dunder Methods)¶
Magic methods (double-underscore or "dunder" methods) let classes integrate with Python's built-in operations: len(), str(), +, [], iteration, context managers, and more. They define how objects behave with operators and built-in functions.
Key Facts¶
- Magic methods are called by Python internals, not directly (
len(obj)callsobj.__len__()) __repr__for developers (unambiguous);__str__for users (readable)__eq__enables==; also required for correctset/dictbehavior (with__hash__)__enter__/__exit__implement the context manager protocol (withstatement)__getitem__/__setitem__enableobj[key]syntax- Never invent your own dunder names - they are reserved for Python
Patterns¶
String Representation¶
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self): # for developers, unambiguous
return f"Point({self.x}, {self.y})"
def __str__(self): # for users, readable
return f"({self.x}, {self.y})"
p = Point(1, 2)
repr(p) # "Point(1, 2)"
str(p) # "(1, 2)"
print(p) # "(1, 2)" - uses __str__
Comparison¶
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return self.amount == other.amount
def __lt__(self, other):
return self.amount < other.amount
def __le__(self, other):
return self.amount <= other.amount
def __hash__(self):
return hash(self.amount)
Use @functools.total_ordering to auto-generate __le__, __gt__, __ge__ from __eq__ + __lt__.
Arithmetic¶
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other): # self + other
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar): # self * scalar
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar): # scalar * self
return self.__mul__(scalar)
def __abs__(self): # abs(self)
return (self.x**2 + self.y**2) ** 0.5
def __len__(self): # len(self)
return 2
Container Protocol¶
class DataStore:
def __init__(self):
self._data = {}
def __getitem__(self, key): # obj[key]
return self._data[key]
def __setitem__(self, key, value): # obj[key] = value
self._data[key] = value
def __delitem__(self, key): # del obj[key]
del self._data[key]
def __contains__(self, key): # key in obj
return key in self._data
def __len__(self): # len(obj)
return len(self._data)
Callable Objects¶
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, x): # obj(x) - makes instance callable
return x * self.factor
double = Multiplier(2)
double(5) # 10
Context Manager Protocol¶
class Timer:
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.elapsed = time.time() - self.start
return False # don't suppress exceptions
with Timer() as t:
heavy_work()
print(t.elapsed)
Iterator Protocol¶
class Range:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
val = self.current
self.current += 1
return val
Common Magic Methods Reference¶
| Method | Triggered By | Purpose |
|---|---|---|
__init__ | Class() | Initialize instance |
__repr__ | repr() | Developer string |
__str__ | str(), print() | User string |
__len__ | len() | Collection size |
__getitem__ | obj[key] | Index/key access |
__setitem__ | obj[key] = v | Index/key assignment |
__contains__ | in | Membership test |
__iter__ | for x in obj | Iteration |
__next__ | next() | Next iteration value |
__call__ | obj() | Make callable |
__enter__/__exit__ | with | Context manager |
__eq__ | == | Equality |
__lt__ | < | Less than |
__hash__ | hash() | Hashability |
__add__ | + | Addition |
__bool__ | bool(), if | Truthiness |
Gotchas¶
- If you define
__eq__, Python sets__hash__toNone(unhashable) - define__hash__explicitly for set/dict use __repr__is the fallback when__str__is not defined__del__is the finalizer (not destructor) - not guaranteed to be called; avoid for cleanup (use context managers)__new__creates the object;__init__initializes it - rarely need to override__new__
See Also¶
- oop fundamentals - classes, methods, properties
- oop advanced - descriptors, metaclasses
- error handling - context manager protocol
- iterators and generators - iterator protocol