Codectionary / Developer documentation / Python

Magic (Dunder) Methods

Magic methods, also called dunder (double underscore) methods, let your custom classes hook into Python's built-in syntax and behavior. __init__ runs on object creation, __str__ controls how an object is displayed with print(), __eq__ and __lt__ control comparisons, __len__ controls the len() function, and __add__ lets objects respond to the + operator. Implementing these makes your custom classes feel like natural, first-class Python types.

Syntax

def __method__(self, ...):

Examples

__init__ and __str__

The constructor and the method controlling how an object is printed.

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        return f"{self.title} by {self.author}"

book = Book("Clean Code", "Robert Martin")
print(book)  # Clean Code by Robert Martin (uses __str__)

__eq__ and __lt__

Defining what equality and ordering mean for custom objects.

class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages

    def __eq__(self, other):
        return self.pages == other.pages

    def __lt__(self, other):
        return self.pages < other.pages

a = Book("Book A", 200)
b = Book("Book B", 350)

print(a == b)  # False
print(a < b)   # True
print(sorted([b, a], key=lambda book: book.pages))

__len__ and __getitem__

Making a custom class support len() and indexing, just like a list.

class Playlist:
    def __init__(self, songs):
        self.songs = songs

    def __len__(self):
        return len(self.songs)

    def __getitem__(self, index):
        return self.songs[index]

playlist = Playlist(["Song A", "Song B", "Song C"])
print(len(playlist))     # 3
print(playlist[1])       # Song B

__add__ and Operator Overloading

Letting the + operator work naturally on instances of your class.

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 __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)  # Vector(4, 6)

Best practices

  • Implement __repr__ (for developers/debugging) alongside __str__ (for end users) - __repr__ should ideally be unambiguous enough to recreate the object
  • Only implement the dunder methods that genuinely make sense for your class - not every class needs __add__ or __lt__
  • When you implement __eq__, consider also implementing __hash__ if instances need to be used in sets or as dictionary keys
  • Use functools.total_ordering to get all comparison operators automatically once you have defined __eq__ and one ordering method

Runtime & versions

Python interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

Python language reference

Specifications & further reading