Special methods, often called dunder methods (double underscore), are the mechanism Python uses to implement operator overloading and built-in function behavior for custom objects. By defining these methods, you can make your classes behave like standard types (e.g., using + to add objects, len() to get length, or print() to show a readable string).
This guide explains how to define the most essential special methods to create Pythonic classes.
Understanding Dunder Methods
Special methods are predefined methods starting and ending with double underscores (e.g., __init__). You do not call them directly (e.g., obj.__len__()). Instead, Python calls them when you use corresponding built-in functions (e.g., len(obj)).
Initialization (__init__)
The most common special method is __init__, which initializes a new instance of a class.
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
p1 = Product("Laptop", 1000)
print(p1.name)Output:
LaptopString Representation (__str__ vs __repr__)
These methods control how your object looks when printed.
__str__: Should be readable for end-users (used byprint()).__repr__: Should be unambiguous for developers (used in the interactive shell or debugger).
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __str__(self):
return f"{self.name} costs ${self.price}"
def __repr__(self):
return f"Product(name='{self.name}', price={self.price})"
p = Product("Mouse", 25)
print(str(p)) # Calls __str__
print(repr(p)) # Calls __repr__Output:
Mouse costs $25
Product(name='Mouse', price=25)Operator Overloading (__add__, __eq__)
You can define how operators like +, -, ==, or < work with your objects.
Overloading Addition (+)
To allow obj1 + obj2, define __add__.
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 __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(1, 4)
v3 = v1 + v2
print(v3)Output:
Vector(3, 7)Overloading Equality (==)
To allow custom comparison logic, define __eq__.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Vector):
return False
return self.x == other.x and self.y == other.y
v1 = Vector(2, 3)
v2 = Vector(2, 3)
print(f"Are vectors equal? {v1 == v2}")Output:
Are vectors equal? TrueContainer Emulation (__len__, __getitem__)
To make your class behave like a list or dictionary (indexing, iteration, length), implement container methods.
class Team:
def __init__(self, members):
self.members = members
def __len__(self):
return len(self.members)
def __getitem__(self, index):
return self.members[index]
team = Team(["Alice", "Bob", "Charlie"])
print(f"Team Size: {len(team)}")
print(f"First Member: {team[0]}")
for member in team:
print(f"- {member}")Output:
Team Size: 3
First Member: Alice
- Alice
- Bob
- CharlieConclusion
Defining special methods makes your classes integrate seamlessly with Python's syntax.
- Always define
__repr__for debugging. - Define
__str__if you need a pretty output for users. - Implement
__eq__if you want to compare object values rather than memory identities. - Use arithmetic methods (
__add__, etc.) only when mathematical operations make logical sense for your object.