โœจDunder Methods: __str__, __repr__, __eq__, __len__LESSON

Dunder Methods: Making Your Objects Feel Like Python

Dunder methods (double underscore methods, also called "magic methods" or "special methods") let your objects integrate with Python's built-in syntax and functions. When Python sees len(obj), it calls obj.__len__(). When it sees a + b, it calls a.__add__(b). By implementing these methods, your classes become first-class citizens in the Python ecosystem.

str vs repr

These are the two most important dunder methods for any class:

  • __repr__(self): Developer representation. Should ideally return a string that could recreate the object. Used in the REPL, debugging, and logging. Always implement this one.
  • __str__(self): User-friendly representation. Used by print() and str(). Falls back to __repr__ if not defined.

eq and Comparison Methods

By default, == compares object identity (same as is). Define __eq__ to compare by value:

Return NotImplemented (not NotImplementedError) when the comparison doesn't make sense โ€” Python will then try the reversed operation on the other operand.

Ordering: lt, le, gt, ge

Define all six comparison methods, or use @functools.total_ordering with just __eq__ and one of __lt__/__le__/__gt__/__ge__:

len and bool

__len__ makes len(obj) work. __bool__ controls truthiness. If you define __len__ but not __bool__, Python uses len(obj) != 0 for truth testing.

contains

__contains__ makes the in operator work:

iter and getitem

These make your object iterable. Implement __iter__ to return an iterator, or __getitem__ for sequence-style access (Python will create an iterator from it automatically):

add, mul, and Arithmetic

hash

If you define __eq__, Python sets __hash__ = None, making your object unhashable. Define __hash__ to make it usable in sets and dicts:

Only make objects hashable if they are immutable (or if equal objects will always hash the same). Mutable objects should generally not be hashable.

Quick Reference

Dunder MethodTriggered By
__repr__repr(x), REPL display
__str__str(x), print(x)
__eq__x == y
__lt__, __le__, etc.x < y, x <= y, etc.
__len__len(x)
__bool__bool(x), if x
__contains__item in x
__iter__for item in x, list(x)
__getitem__x[i]
__add__x + y
__mul__x * y
__hash__hash(x), sets, dict keys

Knowledge Check

Why should `__eq__` return `NotImplemented` (not raise an error) when it can't compare with the other object?

If you define `__eq__` on a class but not `__hash__`, what happens?

What does `@functools.total_ordering` do when used with `__eq__` and `__lt__`?