Magic methods (also called dunder methods) are special methods in Python.
They follow this naming pattern:
Double underscores → __method__
Examples:
__init__
__str__
__repr__
__add__
__len__
These methods define how objects behave.
Magic methods are Python’s hidden hooks into object behavior.
When you write:
obj + other
Python internally calls:
obj.__add__(other)
Operators and built-ins are powered by magic methods.
✔ Customize object behavior
✔ Operator overloading
✔ Clean syntax
✔ Pythonic APIs
✔ Built-in type simulation
✔ Deep language integration
Core of Python OOP.
Everything in Python works via magic methods.
Operators → Magic Methods
Functions → Magic Methods
Built-ins → Magic Methods
class Person:
def __init__(self, name):
self.name = name
✔ Called when object is created.
p = Person("Alice")
Internally:
Person.__init__(p, "Alice")
Controls:
print(object)
Example:
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Person: {self.name}"
p = Person("Alice")
print(p)
Output:
Person: Alice
Used for debugging.
repr(object)
Example:
def __repr__(self):
return f"Person('{self.name}')"
✔ Should be unambiguous.
| Operator | Magic Method |
|---|---|
| + | add |
| - | sub |
| * | mul |
| / | truediv |
Example:
class Number:
def __init__(self, value):
self.value = value
def __add__(self, other):
return Number(self.value + other.value)
n1 = Number(10)
n2 = Number(20)
print((n1 + n2).value)
Output:
30
| Operator | Magic Method |
|---|---|
| == | eq |
| < | lt |
| > | gt |
Example:
def __eq__(self, other):
return self.value == other.value
✔ Enables object comparisons.
Controls:
len(object)
Example:
class Collection:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
c = Collection([1, 2, 3])
print(len(c))
Output:
3
| Behavior | Magic Method |
|---|---|
| Indexing | getitem |
| Assignment | setitem |
| Membership | contains |
Example:
def __getitem__(self, index):
return self.items[index]
✔ Enables:
obj[0]
Allows objects to behave like functions.
Example:
class Greeter:
def __call__(self):
print("Hello!")
g = Greeter()
g()
Output:
Hello!
| Method | Purpose |
|---|---|
| enter | Setup |
| exit | Cleanup |
Example:
class FileManager:
def __enter__(self):
print("Opening resource")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Closing resource")
with FileManager():
print("Using resource")
Output:
Opening resource
Using resource
Closing resource
Avoid:
obj.__add__(other)
✔ Use operators instead.
return self.value + other.value # Bad
✔ Usually return an object.
Always validate other.
if not isinstance(other, ClassName):
return NotImplemented
| Method | Purpose |
|---|---|
| str | User-friendly |
| repr | Developer/debug |
Incorrect calls inside magic methods can cause crashes.
Returning NotImplemented allows Python fallback logic.
Magic methods define:
✔ Object identity
✔ Operator behavior
✔ Container behavior
✔ Callable behavior
✔ Debugging display
✔ Context management
They form Python’s object protocol engine.
✔ Keep behavior intuitive
✔ Return correct types
✔ Use type checks
✔ Use NotImplemented properly
✔ Implement repr for debugging
✔ Avoid overengineering
✔ Magic methods = Dunder methods
✔ Control object behavior
✔ Operators call magic methods
✔ Built-ins rely on magic methods
✔ Enable Pythonic APIs
✔ Extremely powerful
- Implement str
- Implement repr
- Overload + operator
- Overload comparison operator
- Implement len
- Implement getitem
- Implement callable object (call)
- Build context manager