Magic methods in Python: giving your objects native behaviour

A magic method carries double underscores on each side: Python calls it for you the moment len, ==, a for loop or a with block meet your object.
5 min read
Believemy logo

Definition

A class you write always starts out poorer than Python's own types. Len, ==, a for loop all work on a dictionary or a list, never on a handmade object until you add it yourself. Magic methods close that gap by plugging your class into that same syntax.

A magic method is a method whose name starts and ends with two underscores, such as __len__ or __eq__. You never call it yourself: Python triggers it on your behalf as soon as a piece of language syntax meets your object. Writing len(basket) amounts to asking for basket.__len__(), and the class defining it gains a behaviour the built-in types have always had, as this basket shows: it answers len only because it defines __len__, nothing more.

PYTHON
class Basket:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        # This method answers what len(basket) is asking for
        return len(self.items)

basket = Basket(["pen", "notebook"])
print(len(basket))
# 2

They are also called special methods, or dunder methods in most documentation, short for double underscore. The word magic only describes the effect felt, never the mechanism: it is a naming convention the interpreter knows by heart and checks at the right moment.


The families worth knowing

Python defines well over a hundred magic methods, a number that can feel overwhelming: do you need to know them all before writing a useful class? No, a dozen cover the essentials, gathered in the table below.

MethodTriggered by
__init__Creating an object
__repr__ and __str__repr and display
__eq__The equality operator
__lt__Strict comparison, and therefore sorting
__getitem__Square bracket access
__contains__The membership test
__iter__The for loop and anything that walks
__enter__ and __exit__The with block
__call__Parentheses placed after the object
Good to know

Sorting a list of objects calls in theory for several of these comparison methods. The total_ordering decorator from the functools module fills in the missing ones from __eq__ and just one other, for instance __lt__: two methods are then enough where six would otherwise be needed.

An object becomes walkable the moment it exposes __iter__, whether or not it inherits from a list. A context manager is nothing more than an object exposing __enter__ and __exit__, exactly what duck typing describes: what counts is not the class you inherit from, but what your object knows how to do when asked.


The trap of equality without hashing

Adding an __eq__ method looks like it commits you to nothing: you describe how two objects resemble each other. Yet that line carries a side effect almost nobody expects: Python silently removes the default hashing from the class, and your objects can no longer sit in a set or serve as a dictionary key.

PYTHON
class Client:
    def __init__(self, email):
        self.email = email

    def __eq__(self, other):
        # Two clients are equal if their email is, nothing else
        return self.email == other.email

{Client("mary@example.com")}
# TypeError: unhashable type: 'Client'
Warning

The error never shows up when you write __eq__, it waits for the day someone stores your objects in a set or a dict. The TypeError: unhashable type message then points to a line that has nothing to do with the real cause.

The fix is one more method, __hash__, computed on the same attributes as the equality. Python enforces that consistency: two equal objects must produce the same hash, otherwise a dictionary could store the same key twice without ever noticing. A dataclass declared with frozen=True generates both methods for you, and that is often the best answer.


Two representations for two readers

Without writing anything, printing one of your objects in the console returns a line like <Client object at 0x7f...>, which says nothing about what the object holds. Two magic methods let you take back control of that text, and they do not address the same reader.

__repr__ speaks to the developer: it shows up in the console, in error messages and when a list of objects is displayed, and it should reveal what the object is made of. __str__ speaks to the end user, and it is the one an ordinary display produces.

PYTHON
class Duration:
    def __init__(self, minutes):
        self.minutes = minutes

    def __repr__(self):
        # For debugging: show the raw value
        return f"Duration(minutes={self.minutes})"

    def __str__(self):
        # For display: show a readable format
        return f"{self.minutes // 60} h {self.minutes % 60}"

If you write only one of them, write __repr__: Python falls back on it when __str__ is missing, whereas the reverse is never true. A class with no representation prints a memory address, which tells you nothing while debugging and turns a list of results into guesswork.


Frequently asked questions

Question

Can a magic method be called directly?

Technically yes, basket.__len__() works. In practice, better never to: the language syntax raises a clear TypeError when the method is missing, whereas the direct call produces an attribute error that is far harder to make sense of.

Question

How many should a class define?

As few as possible, and __repr__ nearly always since it helps debugging at no cost. A magic method only makes sense when the syntax it captures really says something about the object: giving addition to something nobody adds up in real life makes the code unreadable.

Question

Why does my object refuse to be walked through by a loop?

Because it defines neither __iter__ nor __getitem__, the only two doors Python tries before giving up and raising an error.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.