🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
REFERENCEpython

python Documentation

LOADING ENGINE...

Dunder Methods

AI & DATA SCIENCE // dunder-methods

Dunder methods (short for 'double underscore', like __init__ and __str__) are special methods Python calls automatically to implement built-in behaviors on your own classes.

Syntax

class MyClass:
    def __str__(self): ...
    def __eq__(self, other): ...
    def __len__(self): ...
    def __add__(self, other): ...

Deep Dive Course

Dunder methods are how Python's built-in operators and functions integrate with custom classes: defining __str__ customizes what str()/print() show, __eq__ customizes ==, __len__ customizes len(), __add__ customizes the + operator, and so on for dozens of others. This system, often called 'operator overloading' or adherence to Python's 'data model', is what makes it possible for a custom class to feel like a natural part of the language, supporting iteration, indexing, comparison, or arithmetic, rather than requiring special-cased method calls.

1Understanding Dunder Methods

Dunder methods are how Python's built-in operators and functions integrate with custom classes: defining __str__ customizes what str()/print() show, __eq__ customizes ==, __len__ customizes len(), __add__ customizes the + operator, and so on for dozens of others. This system, often called 'operator overloading' or adherence to Python's 'data model', is what makes it possible for a custom class to feel like a natural part of the language, supporting iteration, indexing, comparison, or arithmetic, rather than requiring special-cased method calls.

💡

Define __repr__ on every class you write, even if you also define __str__ — __repr__ is the fallback used by the REPL, debuggers, and inside containers like lists, and a good one, ideally showing how to recreate the object, makes debugging significantly easier.

editor.html
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, 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})"

print(Vector(1, 2) + Vector(3, 4))
localhost:3000

2Practical Example

Here is a real-world application of Dunder Methods showing how it is used in production Python code.

editor.html
class Playlist:
    def __init__(self, songs):
        self.songs = songs
    def __len__(self):
        return len(self.songs)
    def __getitem__(self, index):
        return self.songs[index]

p = Playlist(["Song A", "Song B", "Song C"])
print(len(p))
print(p[1])
localhost:3000

3Best Practices

Follow these guidelines when working with Dunder Methods:

1. Implement __repr__ on custom classes for better debugging output in the REPL, logs, and inside containers

2. Implement __eq__, and __hash__ alongside it if instances need to be hashable, when instances should be compared by value instead of identity

3. Only implement the dunder methods your class's usage actually calls for — implementing __add__ on a class where addition doesn't make sense adds confusing, unused surface area

⚠️

Tip: Define __repr__ on every class you write, even if you also define __str__ — __repr__ is the fallback used by the REPL, debuggers, and inside containers like lists, and a good one, ideally showing how to recreate the object, makes debugging significantly easier.

editor.html
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, 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})"

print(Vector(1, 2) + Vector(3, 4))
localhost:3000

Examples

Example 01Basic Usage
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, 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})"

print(Vector(1, 2) + Vector(3, 4))
Example 02Advanced Example
class Playlist:
    def __init__(self, songs):
        self.songs = songs
    def __len__(self):
        return len(self.songs)
    def __getitem__(self, index):
        return self.songs[index]

p = Playlist(["Song A", "Song B", "Song C"])
print(len(p))
print(p[1])

Best Practices

  • Implement __repr__ on custom classes for better debugging output in the REPL, logs, and inside containers
  • Implement __eq__, and __hash__ alongside it if instances need to be hashable, when instances should be compared by value instead of identity
  • Only implement the dunder methods your class's usage actually calls for — implementing __add__ on a class where addition doesn't make sense adds confusing, unused surface area

Interview Question

How does implementing __getitem__ on a custom class make it work with both indexing and a for loop, even without defining __iter__?

Hint: Think about Python's fallback behavior for iteration.

Defining __getitem__ directly enables the square-bracket indexing syntax. For iteration, Python has a legacy fallback: if a class defines __getitem__ but not __iter__, a for loop will call __getitem__ with increasing integer indices, starting at 0, until it raises an IndexError, at which point the loop stops. This lets a class support iteration through __getitem__ alone, though implementing a proper __iter__ is the more modern and explicit approach for new code.

Exercises

MediumPractice using Dunder Methods in a real scenario.
View Solution
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, 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})"

print(Vector(1, 2) + Vector(3, 4))

Frequently Asked Questions

How does implementing __getitem__ on a custom class make it work with both indexing and a for loop, even without defining __iter__?

Defining __getitem__ directly enables the square-bracket indexing syntax. For iteration, Python has a legacy fallback: if a class defines __getitem__ but not __iter__, a for loop will call __getitem__ with increasing integer indices, starting at 0, until it raises an IndexError, at which point the loop stops. This lets a class support iteration through __getitem__ alone, though implementing a proper __iter__ is the more modern and explicit approach for new code.

Related Functions

initclass-keywordpolymorphism