Magic methods (also known as dunder methods, for “double underscore”) are special methods in Python that enable operator overloading and hook into built-in behaviors.

🔹 __init__: Object Constructor
Called when an object is created.
class Dog:
def __init__(self, name):
self.name = name
dog = Dog("Fido")
print(dog.name) # Fido
🔹 __str__ and __repr__: String Representations
class Dog:
def __init__(self, name): self.name = name
def __str__(self): return f"Dog named {self.name}"
def __repr__(self): return f"Dog({self.name!r})"
dog = Dog("Fido")
print(dog) # Dog named Fido
print(repr(dog)) # Dog('Fido')
🔹 __len__: Length of Object
Used by len(obj)
class Basket:
def __init__(self, items): self.items = items
def __len__(self): return len(self.items)
b = Basket(["apple", "banana"])
print(len(b)) # 2
🔹 __getitem__, __setitem__, __delitem__: Indexing
class Container:
def __init__(self): self.data = {}
def __getitem__(self, key): return self.data[key]
def __setitem__(self, key, value): self.data[key] = value
def __delitem__(self, key): del self.data[key]
c = Container()
c['x'] = 42
print(c['x']) # 42
del c['x']
🔹 __call__: Makes Object Callable Like a Function
class Greeter:
def __call__(self, name): return f"Hello, {name}!"
greet = Greeter()
print(greet("Alice")) # Hello, Alice!
🔹 __eq__, __lt__, etc.: Comparisons
class Point:
def __init__(self, x): self.x = x
def __eq__(self, other): return self.x == other.x
def __lt__(self, other): return self.x < other.x
a = Point(1)
b = Point(2)
print(a == b) # False
print(a < b) # True
🔹 __add__, __sub__, __mul__, etc.: Operator Overloading
class Vector:
def __init__(self, x): self.x = x
def __add__(self, other): return Vector(self.x + other.x)
def __repr__(self): return f"Vector({self.x})"
v1 = Vector(3)
v2 = Vector(4)
print(v1 + v2) # Vector(7)
🔹 __enter__ and __exit__: Context Manager (with)
class FileManager:
def __enter__(self):
print("Open resource")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Close resource")
with FileManager():
print("Using resource")
# Output:
# Open resource
# Using resource
# Close resource
🔹 __getattr__ and __setattr__: Dynamic Attribute Access
class Dynamic:
def __getattr__(self, name): return f"No attribute '{name}'"
def __setattr__(self, name, value):
print(f"Setting {name} = {value}")
super().__setattr__(name, value)
d = Dynamic()
print(d.foo) # No attribute 'foo'
d.x = 10 # Setting x = 10
🔹 __del__: Object Destructor (⚠️ rarely needed)
class Temp:
def __del__(self):
print("Object destroyed")
t = Temp()
del t # Object destroyed (maybe)
🔹 __new__: Call the constructor (⚠️ rarely needed)
__new__ is called before __init__ and is responsible for actually creating the instance of the class. It’s most useful when:
- You’re subclassing immutable types (
int,str,tuple) - You need to control the instantiation process (e.g., singleton pattern)
Subclassing immutable types:
class MyInt(int):
def __new__(cls, value):
return super().__new__(cls, value + 1)
x = MyInt(5)
print(x) # 6
Singleton design pattern:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
a = Singleton()
b = Singleton()
print(a is b) # True
__new__ vs __init__
| _new__ | __init__ |
| Creates the object (allocates memory) | Initializes the object after it’s created |
| First method called during instantiation | Second method called |
| Used for immutable types and special creation | Used for setting attributes |
Cheatsheet of magic methods
| Purpose | Method |
| Constructor | __new__, __init__ |
| String display | __str__, __repr__ |
| Collections | __iter__, __next__ |
| Operators | __add__, __eq__, etc. |
| Iteration | __iter__, __next__ |
| Context manager | __enter__, __exit__ |
| Attribute access | __getattr__, __setattr__ |

Early in my career, I specialized in the Python language. Python has been a constant in my professional life for over 10 years now. In 2018, I moved to London where I worked at companies of various sizes as a Python developer for five years. In parallel, I developed my activity as a Mentor, to which I now dedicate myself full-time.