7 min read
ā¢Question 20 of 41mediumMagic Methods (Dunder)
Special methods in Python classes.
What You'll Learn
- What magic/dunder methods are
- String representation: str vs repr
- Operator overloading for custom classes
- Container and iteration protocols
- Callable objects and context managers
Understanding Magic Methods
Magic methods (also called dunder methods for "double underscore") are special methods that Python calls automatically in response to certain operations. They let you customize how your objects behave with built-in functions and operators.
String Representation
code.pyPython
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""Human-readable string (for print, str())"""
return f"{self.name}, {self.age} years old"
def __repr__(self):
"""Developer-readable string (for debugging, repr())"""
return f"Person('{self.name}', {self.age})"
p = Person("Alice", 30)
print(p) # Alice, 30 years old (__str__)
print(repr(p)) # Person('Alice', 30) (__repr__)
print([p]) # [Person('Alice', 30)] (uses __repr__ in containers)Comparison Operators
code.pyPython
from functools import total_ordering
@total_ordering # Generates other comparisons from __eq__ and __lt__
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 __hash__(self):
return hash(self.amount)
m1 = Money(100)
m2 = Money(200)
print(m1 < m2) # True
print(m1 <= m2) # True (from @total_ordering)
print(m1 == m2) # FalseArithmetic Operators
code.pyPython
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar):
"""Right multiplication: scalar * vector"""
return self.__mul__(scalar)
def __neg__(self):
return Vector(-self.x, -self.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(1, 1)
print(v1 + v2) # Vector(3, 4)
print(v1 - v2) # Vector(1, 2)
print(v1 * 2) # Vector(4, 6)
print(3 * v1) # Vector(6, 9) (uses __rmul__)
print(-v1) # Vector(-2, -3)Container Protocol
code.pyPython
class Deck:
def __init__(self):
self.cards = ['A', '2', '3', '4', '5']
def __len__(self):
return len(self.cards)
def __getitem__(self, index):
return self.cards[index]
def __setitem__(self, index, value):
self.cards[index] = value
def __delitem__(self, index):
del self.cards[index]
def __contains__(self, item):
return item in self.cards
deck = Deck()
print(len(deck)) # 5
print(deck[0]) # 'A'
print('A' in deck) # True
deck[0] = 'K' # Uses __setitem__Callable Objects
code.pyPython
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, x):
return x * self.factor
double = Multiplier(2)
triple = Multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
print(callable(double)) # TrueContext Managers
code.pyPython
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
self.elapsed = time.time() - self.start
print(f"Elapsed: {self.elapsed:.4f}s")
return False # Don't suppress exceptions
with Timer():
sum(range(1000000))
# Elapsed: 0.0234sCommon Magic Methods Reference
| Method | Triggered By |
|---|---|
__init__ | Object creation |
__str__ | str(), print() |
__repr__ | repr(), debugger |
__eq__, __lt__ | ==, < |
__add__, __sub__ | +, - |
__mul__, __truediv__ | *, / |
__len__ | len() |
__getitem__ | obj[key] |
__setitem__ | obj[key] = value |
__contains__ | in operator |
__iter__ | for loop, iter() |
__call__ | obj() |
__enter__, __exit__ | with statement |
__hash__ | hash(), dict keys |
Interview Tip
When asked about magic methods:
- str for humans, repr for developers
- Use @total_ordering with eq and lt
- rmul for right-side operations (2 * obj)
- call makes instances callable
- enter/exit for context managers
- Implement hash if overriding eq