Python Dunder Methods: Mastering the Hooks of the Data Model
Overview of the Python Data Model
Python's flexibility stems from its core philosophy: everything is an object. Whether you handle a string, an integer, or a custom class, the
Prerequisites
To follow this guide, you should understand
Key Libraries & Tools
- Standard Library: No external packages are required as dunder methods are built into the core language.
- timeit: Used for performance benchmarking to ensure custom implementations don't slow down your code.
Code Walkthrough: Implementing Custom Behavior
Let's look at how to use __repr__ and __add__ to make a custom Vector class feel like a native type.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
# Provides a developer-friendly string representation
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
# Implements operator overloading for the + sign
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Output: Vector(4, 6)
In this example, __init__ handles object setup, while __repr__ ensures that when we print the object, we see its data rather than a cryptic memory address. The __add__ method enables operator overloading, allowing us to use the + sign between two objects intuitively.
When to Use Dunder Methods
Override these methods when you need your custom objects to emulate built-in types. For instance, if you build a Bank class, implementing __len__ and __getitem__ allows users to check the number of accounts and access them using square brackets (bank[0]). Another vital use case is creating Context Managers using __enter__ and __exit__. This ensures resources like database connections close automatically after use, promoting cleaner code via the with statement.
When to Avoid Overriding
You should avoid dunder methods if they violate the Principle of Least Astonishment. If you override __new__ to return an object of a different class, you confuse other developers who expect a standard instance. Performance is another concern. Avoid using str(self) inside a comparison method like __eq__. Converting objects to strings for every comparison can make your code 10 times slower compared to direct attribute checks.
Syntax Notes
- Always use the double underscore prefix and suffix (e.g.,
__iter__). - Never create your own dunder names; stick to those defined in the official documentation to avoid conflicts with future language updates.
Tips & Gotchas
- Readability first: If a standard method name like
validate()is clearer than using__call__, choose the standard method. - Inheritance: Remember that
__eq__only checks for value equality if you implement it; otherwise, it defaults to checking if two variables point to the same object in memory.

Fancy watching it?
Watch the full video and context