Skip to main content

Python Dunder Methods: Special Commands Worth Knowing

Learn how Python's double underscore methods allow your custom objects to work naturally with built-in operations, from printing to arithmetic. Discover how these magic methods simplify class design and unlock powerful behaviors.
Apr 4, 2025  · 8 min read

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:


Oluseye Jeremiah's photo
Author
Oluseye Jeremiah
LinkedIn

Tech writer specializing in AI, ML, and data science, making complex ideas clear and accessible.

Topics

Learn Python with DataCamp

Course

Intermediate Python

4 hr
1.2M
Level up your data science skills by creating visualizations using Matplotlib and manipulating DataFrames with pandas.
See DetailsRight Arrow
Start Course
See MoreRight Arrow
Related
Data Skills

Tutorial

Introducing Python Magic Methods

Discover what magic methods is while looking at the difference between methods and functions.
Kurtis Pykes 's photo

Kurtis Pykes

11 min

Tutorial

Role of Underscore(_) in Python Tutorial

In this tutorial, you're going to learn about the uses of underscore(_) in python.
Hafeezul Kareem Shaik's photo

Hafeezul Kareem Shaik

8 min

Tutorial

Object-Oriented Programming in Python (OOP): Tutorial

Tackle the basics of Object-Oriented Programming (OOP) in Python: explore classes, objects, instance methods, attributes and much more!
Théo Vanderheyden's photo

Théo Vanderheyden

12 min

Tutorial

Python Print() Function

Learn how you can leverage the capability of a simple Python Print function in various ways with the help of examples.
Aditya Sharma's photo

Aditya Sharma

10 min

Tutorial

Python Classes Tutorial

In Python, everything is an object. Numbers, strings, DataFrames, even functions are objects. In particular, everything you deal with in Python has a class, a blueprint associated with it under the hood.
DataCamp Team's photo

DataCamp Team

3 min

Tutorial

Usage of Asterisks in Python

Many Python users are familiar with using asterisks for multiplication and power operators, but in this tutorial, you'll find out additional ways on how to apply the asterisk.
Hafeezul Kareem Shaik's photo

Hafeezul Kareem Shaik

9 min

See MoreSee More