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

At a glance

Purpose
Scripting and general-purpose applications
File extension
.py
Runs in
Python interpreter
Usually used with
Python standard library and packages

Specifications & further reading

Related Python documentation

class
The class keyword defines a blueprint for creating objects in Python. Classes are the foundation of Object-Oriented Programming (OOP), allowing you to bundle data (attributes) and functionality (methods) together. A class defines what attributes an object will have and what operations can be performed on it. Think of a class as a template or a factory for creating objects with similar characteristics.
Inheritance & super()
Inheritance lets a class (the child or subclass) reuse and extend the attributes and methods of another class (the parent or superclass), written as class Child(Parent). The built-in super() function gives access to the parent class's methods from within the child, most commonly used to call the parent's __init__ so the child doesn't have to duplicate its setup logic. Python also supports multiple inheritance, where a class inherits from more than one parent.
@property
The @property decorator lets you define a method that can be accessed like a plain attribute, without parentheses. This is useful for computed values that should look like simple attributes, and for adding validation logic that runs whenever an attribute is set, via a matching @x.setter. Properties let you start with simple public attributes and later add logic without breaking any code that uses the class.
@staticmethod and @classmethod
Regular instance methods automatically receive self, the specific object they were called on. @staticmethod methods receive neither self nor the class - they behave like a plain function that just happens to live inside a class, grouped there for organizational purposes. @classmethod methods receive the class itself (conventionally named cls) instead of an instance, making them useful for alternative constructors that build an instance in a different way.