Last Updated: January 3, 2026
Dunder methods, short for "double underscore methods," are a fascinating part of Python that allow us to define special behaviors for our objects. Think of them as hooks that let us customize how our objects behave with built-in functions and operators.
When you create a class, these methods let you define how instances of that class should react to various operations, like addition, string conversion, and comparison.
Understanding dunder methods is crucial for building intuitive and user-friendly classes. They can help make your objects feel more like native Python types, which enhances both usability and readability. Let’s dive in!
Dunder methods are methods with names that start and end with double underscores, like __init__, __str__, and __add__. These methods are also known as "magic methods" because they enable special behaviors in our objects.
When you call a built-in Python function or operator on an instance of your class, Python looks for the corresponding dunder method. If it finds one, it uses that method to perform the operation. If not, you'll typically get a TypeError.
For instance, when you use the print function on an object, Python checks for the __str__ method. If it’s defined, it will call that method to get a string representation of the object, which is what gets printed.
Here's a simple example:
In this case, defining __str__ allowed us to customize how our object is displayed when printed.
Let’s explore some of the most commonly used dunder methods and understand their applications.
__init__(self, ...): This is the initializer method, but we’ve covered that elsewhere. It sets up the initial state of a new object.__str__(self): Defines a human-readable representation of the object. It's what you see when you call print(obj).__repr__(self): Aimed at providing an "official" string representation of the object, ideally one that could be used to recreate the object. It’s what you see when you call repr(obj).Here’s how both can coexist:
While __str__ is for end-users (more readable), __repr__ is for developers (more technical). This helps us create objects that are easier to work with in different contexts.
One of the powerful features of dunder methods is operator overloading. This allows us to define how operators like +, -, and * behave when applied to our objects.
Let’s say you want to create a simple Vector class that can be added together:
Notice that we check if other is an instance of Vector. If it’s not, we return NotImplemented. This is important because it allows Python to handle the operation gracefully if it cannot be performed.
Dunder methods can also be used for comparison operations. Common ones include:
__eq__(self, other): Defines behavior for ==.__lt__(self, other): Defines behavior for <.__le__(self, other): Defines behavior for <=.Let’s implement a simple Rectangle class with comparison capabilities:
Implementing these methods allows us to use Python's built-in operators to work with our objects seamlessly, making the code more readable and intuitive.
Dunder methods also allow our objects to be iterable and to work with context managers.
To make an object iterable, we implement __iter__ and __next__ methods. Here’s a simple example with a custom range:
To create objects that can be used in with statements, we need to define __enter__ and __exit__:
This will output:
By implementing iteration and context management, we design our classes to integrate more closely with Python's ecosystem, which can lead to cleaner and more efficient code.
Beyond the common ones, there are advanced dunder methods that can enhance our classes further:
__call__(self, ...): Allows an instance of a class to be called like a function.__getitem__(self, key): Enables indexing and slicing.__setitem__(self, key, value): Defines behavior for setting values with an index.Here’s a quick example of a callable object:
These advanced dunder methods can greatly enhance the functionality of your classes, making them more versatile and powerful in various programming contexts.
Now that you understand dunder methods and their various applications, you are ready to explore __str__ and __repr__.
In the next chapter, we will look at how to effectively implement these methods to create meaningful string representations of your objects, enhancing both usability and debugging.