Academy
ProgrammingPython AdvancedDunder Methods

Dunder Methods

The Secret Behind Built-in Behaviour

When you write len([1, 2, 3]), Python calls list.__len__(). When you write "abc" + "def", Python calls str.__add__(). When you write 10 > 5, Python calls int.__gt__().

Every operator, every built-in function, every piece of syntax you've been using is backed by a dunder method — a method with double underscores on both sides. You've already seen two: __init__ and __str__. There are dozens more.

By implementing them in your own classes, you make your objects feel native to the language. They work with len(), with +, with <, with in, with indexing — exactly like built-in types.

__repr__ and __str__

You already know __str__ controls what print() shows. __repr__ is its complement — it provides an unambiguous, developer-facing representation, used in the REPL and for debugging.

The rule of thumb: __repr__ should ideally be a string that, if pasted into Python, recreates the object. __str__ is for human display. If you only define one, define __repr__ — Python falls back to it for __str__ when __str__ is absent.

__len__ and __bool__

Once __len__ is defined, len(playlist) works naturally. Once __bool__ is defined, if playlist: works — truthy when non-empty, falsy when empty. If __bool__ is not defined but __len__ is, Python uses len(obj) != 0 as the boolean value automatically.

Comparison Operators

Define these to make your objects comparable with ==, <, >, and so on:

DunderOperator
__eq__==
__ne__!=
__lt__<
__le__<=
__gt__>
__ge__>=

Defining __lt__ and __eq__ is enough to make sorted() work on your objects. Python's sort uses __lt__ for comparisons.

A shortcut: from functools import total_ordering lets you define only __eq__ and one comparison method, and Python fills in the rest automatically.

Arithmetic Operators

v1 + v2 calls v1.__add__(v2). abs(v1) calls v1.__abs__(). Your Vector class now behaves like a native numeric type for these operations.

__getitem__, __setitem__, __contains__

These give your objects indexing (obj[key]), index assignment (obj[key] = value), and in support:

grid[0, 0] = 5 calls grid.__setitem__((0, 0), 5) — the tuple (0, 0) is passed as pos. 9 in grid calls grid.__contains__(9). Your class now supports natural Python syntax.

__call__ — Making Objects Callable

Adding __call__ makes an instance callable like a function:

double(10) calls double.__call__(10). The object behaves exactly like a function — but carries its own state. This is the foundation of callable objects and, in machine learning, of the layer objects in frameworks like PyTorch.

What You Have Learned

Dunder methods are Python's interface for making objects feel native to the language — not special cases, but full participants in its syntax.

The key ideas:

  • Dunders power all operators and built-in functions behind the scenes
  • __repr__ — developer representation; __str__ — human display; define __repr__ first
  • __len__ and __bool__ enable len() and truthiness checks
  • Comparison dunders (__eq__, __lt__, etc.) enable operators and sorted()
  • Arithmetic dunders (__add__, __mul__, etc.) enable operators on custom types
  • __getitem__, __setitem__, __contains__ enable indexing and in
  • __call__ makes an instance callable like a function

In the next lesson, you'll go deeper into comprehensions — dict and set comprehensions, nested comprehensions, and the powerful zip and enumerate tools.