Course
Python Dunder Methods: Special Commands Worth Knowing
Have you ever wondered how Python makes your custom classes work seamlessly with built-in functions? It is thanks to those nifty "dunder" methods—short for double underscore methods.
Imagine you're creating a class for complex numbers and want to use the +
operator naturally. Instead of writing extra code to handle addition, you define an __add__
method, and Python takes care of the rest.
These magic methods, like __init__
for object creation and __str__
for string representation, are automatically invoked by Python, so you don't have to call them directly. They prevent naming conflicts with your regular methods and power features like operator overloading and custom behavior.
Whether instantiating objects, performing arithmetic operations, or simply printing an object, dunder methods quietly work in the background to make your code more elegant and efficient. If you're new to Python and want to catch up on some of the main ideas, take our Introduction to Python course for a good foundation.
What Are Dunder Methods in Python?
As I started to mention, Python dunder methods are unique, or "magic," methods that form the backbone of Python's data model by allowing your custom classes to integrate with Python's built-in operations. These operators include things like printing, comparing, and performing arithmetic.
When you define methods such as __init__
for initializing objects, __repr__
and __str__
for object representation, or even __del__
for cleanup, you're essentially telling Python how to handle standard operations for your objects. The interpreter automatically calls these methods when the corresponding operation is performed. This built-in automation creates a bridge between native Python features and user-defined classes.
Later, we’ll look at examples of classes that use these methods and those that don’t so you can see how all this works.
Categories of Dunder Methods
Let's explore the main categories of dunder methods and see how they empower your code.
1. Initialization and construction
Dunder methods like __new__
, __init__
, and __del__
control how objects are created, initialized, and eventually destroyed.
-
__new__
vs.__init__
:__new__
is responsible for creating a new instance, while__init__
initializes it. This separation is important when working with immutable types, where you might override__new__
for something else. -
__del__
: This method acts as a destructor, allowing for cleanup when an object is about to be collected as garbage.
You can even customize __new__
in immutable classes to enforce specific creation rules.
2. Numeric and arithmetic methods
Dunder methods such as __add__
, __sub__
, __mul__
, and their in-place variants allow your objects to support arithmetic operations. This is known as operator overloading. By defining these methods, you can enable natural arithmetic behavior for your custom objects.
For example, overload __add__
in a Vector
class to add corresponding elements of two vectors. This is particularly useful when designing classes that model mathematical concepts or financial instruments.
3. Comparison and equality methods
Methods like __eq__
, __lt__
, and __gt__
determine how objects are compared. These methods let you define what it means for two objects to be equal or how they should be ordered.
A typical example is comparing the areas of two shapes: you can override __lt__
to return true
if one shape’s area is less than the other’s. This can be helpful in collections or sorting algorithms.
4. String representation methods
The __str__
and __repr__
methods control how your objects are displayed as strings.
-
__repr__
should provide a developer-friendly representation that can be used to recreate the object. -
__str__
focuses on a user-friendly display.
5. Container and iterable methods
To let your objects behave like sequences or containers, you can implement methods such as __len__
, __getitem__
, and __iter__
. This enables operations like indexing, iterating, and membership tests.
For example, if you design a custom stack or list, implementing these methods allows you to use built-in functions like len()
.
6. Callability and functional programming
With the __call__
method, instances of your class can be invoked like functions. This is particularly useful for creating stateful function objects that can cache results or maintain internal state across calls. Think of it as turning your object into a mini-computation engine that you can call repeatedly with different parameters.
7. Context managers
Implementing __enter__
and __exit__
methods allows your objects to be used with the statement for resource management. This is crucial for managing resources like file handles or network connections, ensuring they are correctly set up and cleaned up.
A real-world scenario is using a custom context manager to open and close database connections safely.
8. Attribute access and descriptors
Methods such as __getattr__
, __setattr__
, and __delattr__
let you control how attributes are accessed and modified. The descriptor protocol further refines this by enabling objects to manage attribute access dynamically.
For example, a descriptor might validate or transform attribute values, ensuring they meet specific criteria before being set.
Advanced and Miscellaneous Dunder Methods
While the core dunder methods cover most use cases, Python includes advanced and specialized methods for tasks like asynchronous programming, metaprogramming, and library-specific behavior. For example:
-
Asynchronous methods: Methods like
__aiter__
and__anext__
enable asynchronous iteration, while__await__
supports awaitable objects. -
Metaprogramming hooks: Methods like
__prepare__
and__set_name__
allow dynamic class creation and attribute naming. -
Library-specific methods: Libraries like NumPy or Pandas often define custom dunder methods (e.g.,
__array__
for NumPy integration).
These advanced methods are typically used for specialized tasks so that most developers won't need them daily. However, they showcase Python's flexibility and depth. I recommend exploring the official Python data model documentation for a complete list. This resource provides comprehensive details on all dunder methods and their intended use.
All Dunder Methods in Python
Python provides over 100 dunder methods, each designed to control different aspects of object behavior. While we’ve explored key categories in this article, here’s a high-level summary to serve as a quick reference:
-
Arithmetic methods: Methods like
__add__
,__sub__
,__mul__
, and__truediv__
allow your objects to support operators (+
,-
,*
,/
). -
Comparison methods: Methods such as
__eq__
,__lt__
, and__gt__
define how objects are compared for equality or order. -
Attribute management: With methods like
__getattr__
,__setattr__
, and__delattr__
, you can control attribute access and implement dynamic behaviors. -
Initialization and construction:
__new__
,__init__
, and__del__
manage object creation, initialization, and cleanup, respectively. -
String representation: Methods such as
__str__
and__repr__
determine how objects are represented as strings, aiding user-friendly output. -
Iteration and container behavior: Implement
__iter__
and__next__
to make your objects iterable and other methods to support indexing and length retrieval. -
Callability:
__call__
lets an instance be called like a function, enabling a functional programming style with stateful behavior. -
Context management: With
__enter__
and__exit__
, objects can be used with statements to manage resources properly. -
Metaprogramming hooks: Methods like
__init_subclass__
provide ways to customize class creation and behavior dynamically.
Refer to the official Python Data Model documentation for a complete, detailed list of these dunder methods and their functionalities. This comprehensive resource will help you unlock the full power of Python’s object model and craft more elegant, efficient code.
Real-World Examples & Use Cases
Let's explore how dunder methods can power real-world scenarios through a few examples.
Operator overloading
Imagine you have a custom numeric or string class. By implementing dunder methods like __add__
or __mul__
, you enable your objects to work naturally with arithmetic operators. You can add, subtract, or multiply instances just as you would with built-in types, making your custom classes blend seamlessly with Python's arithmetic operations.
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 __str__(self):
return f"Vector({self.x}, {self.y})"
# Usage
v1 = Vector(1, 2)
v2 = Vector(3, 4)
result = v1 + v2
print(result) # Output: Vector(4, 6)
Caching with callability
Ever wanted to avoid repeating expensive computations? By implementing the __call__
method, you can make an object behave like a function that caches its results. This allows you to store the outcome of a heavy computation and quickly return it on subsequent calls, optimizing performance and saving valuable processing time.
class CachedComputation:
def __init__(self):
self.cache = {}
def __call__(self, x):
if x not in self.cache:
self.cache[x] = self._expensive_computation(x)
return self.cache[x]
def _expensive_computation(self, x):
# Imagine a complex calculation here
return x ** 2
# Usage
compute = CachedComputation()
print(compute(5)) # Computes and caches: Output: 25
print(compute(5)) # Retrieves from cache: Output: 25
Custom context managers
Building a context manager using __enter__
and __exit__
methods lets you automate resource management. For example, you can design a custom context manager to handle file operations—opening a file when you enter a block and ensuring it closes when you exit. This simplifies your code and prevents common errors like resource leaks.
class FileHandler:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
# Usage
with FileHandler("example.txt", "r") as file:
content = file.read()
print(content)
Custom iterables and containers
To make your custom containers work with Python’s loops and comprehensions, implement the iteration protocol by defining methods like __iter__
and __next__
. Whether you’re building a custom stack, queue, or any other container, these methods allow your objects to be looped over just like a regular list, enhancing the flexibility and usability of your classes.
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index >= len(self.items):
raise StopIteration
item = self.items[self.index]
self.index += 1
return item
# Usage
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
for item in stack:
print(item) # Output: 1, 2, 3
For more on custom iterables and container behavior, check out the Python Lists Tutorial and Python List Functions & Methods Tutorial and Examples.
Best Practices with Dunder Methods
When and how to override dunder methods is essential. Here are some guidelines to keep in mind:
When to override
Only override dunder methods if you need to customize behavior. For example, if you want your objects to support arithmetic or equality checks in a specific way, then it's worth implementing methods like __add__
or __eq__
.
Consistency
If you override one method, such as __eq__
for equality checks, update related methods like __hash__
accordingly. This consistency ensures your objects behave correctly in collections like sets or dictionaries.
Avoid overuse
Resist the temptation to create new dunder names outside the standard Python data model. Sticking to the built-in set of dunder methods keeps your code clear and prevents unexpected behavior.
Performance considerations
Keep in mind that overriding dunder methods excessively, especially in frequently run parts of your code, can impact performance. Aim for efficient implementations to avoid any slowdowns in high-frequency operations.
Conclusion and Further Resources
Dunder methods empower your Python classes to interact seamlessly with the language's built-in features, making your code cleaner and more intuitive. They let your objects handle everything from arithmetic and comparisons to custom attribute management without extra effort from you. You should experiment with these methods in your projects and see how they can simplify complex operations.
For more in-depth information, check out the official Python data model documentation. You will also find our DataCamp resources helpful:
- Python Developer career track – Enhance your overall Python expertise.
- Introduction to Python course – A great starting point for learning Python fundamentals.
- Python Lists Tutorial – Dive into data structures and learn more about Python’s powerful built-in types.
- Python lambda Tutorial – Explore functional programming in Python.
Tech writer specializing in AI, ML, and data science, making complex ideas clear and accessible.
Learn Python with DataCamp
Course
Cluster Analysis in Python
Course
Introduction to Python for Developers

Tutorial
Introducing Python Magic Methods
Tutorial
Role of Underscore(_) in Python Tutorial
Tutorial
Object-Oriented Programming in Python (OOP): Tutorial
Tutorial
Python Print() Function
Tutorial
Python Classes Tutorial

DataCamp Team
3 min
Tutorial