Filter concepts by levelShowing all levels.

Python · Dunder methods

Dunder methods

The methods Python calls for you behind +, ==, len(), for, [], with, and a plain function call

  • __init__ / __new____new__ creates the object, __init__ sets it up
  • __str__ / __repr__reader-facing vs. developer-facing text
  • __eq__ / __hash__must stay consistent — equal objects need equal hashes
  • __iter__ / __next__a for loop is exactly this pair, called automatically

1.__new__ and __init__

Name(...) actually runs two steps: __new__(cls, ...) creates and returns a fresh, empty object, then __init__(self, ...) sets up its attributes. __new__ is rarely overridden — almost all classes only ever touch __init__.

Key features

  • Name(...) calls __new__ first to create the object, then __init__ to set it up
  • __new__(cls, ...) is a staticmethod (implicitly) that must return an instance
  • If __new__ returns an instance of cls, Python calls __init__ on it automatically
  • __init__ never returns a value — returning anything but None is a TypeError
  • Overriding __new__ is rare — mainly for immutable-type subclassing and singletons

The two-step construction pipeline

The two-step construction pipeline
StepWhat runs
1. Name(args)Python calls type.__call__, which starts the pipeline
2. __new__(cls, args)creates and returns a new, empty object
3. __init__(self, args)runs on that object, only if step 2 returned a cls instance
Resultthe fully constructed object is handed back to the caller
Name(...) is two steps, not one

__new__(cls)

creates a blank object

__init__(self)

sets up its attributes

Name(...)

returns the finished object

  1. __new__(cls) — creates a blank object
  2. __init__(self) — sets up its attributes
  3. Name(...) — returns the finished object

Command

python
class Name:
    def __new__(cls, *args, **kwargs):
        instance = super().__new__(cls)   # actually allocate
        return instance

    def __init__(self, *args, **kwargs):
        ...                                 # then set up attributes

Name(...) is two steps: __new__ creates the object, __init__ sets it up. Override __new__ only for singletons or immutable-type subclassing.

2.__str__ and __repr__

__repr__ returns an unambiguous, developer-facing string — ideally valid Python that recreates the object. __str__ returns a reader-facing string, used by print()/str()/f-strings. If __str__ is missing, Python falls back to __repr__.

Key features

  • __repr__(self) should be unambiguous — ideally, valid Python that recreates the object
  • __str__(self) returns a readable string for print()/str()/f-strings
  • If __str__ is not defined, Python falls back to __repr__ for print()/str() too
  • The interactive REPL always uses __repr__ to display a bare expression's result
  • Both must return an actual str — returning anything else raises TypeError

Which dunder gets called, and when

Which dunder gets called, and when
SituationMethod called
print(obj), str(obj), f"{obj}"__str__ — falls back to __repr__ if undefined
repr(obj), bare obj in the REPL__repr__ always
Inside a list/dict: print([obj])__repr__ — a container's own repr calls repr() on each item
Neither definedPython's default: <ClassName object at 0x...>
Two audiences, two dunders

__str__

print(obj) — for a reader

__repr__

repr(obj), REPL — for a developer

no __str__?

falls back to __repr__

  1. __str__ — print(obj) — for a reader
  2. __repr__ — repr(obj), REPL — for a developer
  3. no __str__? — falls back to __repr__

Command

python
class Name:
    def __repr__(self):
        return f"Name({self.value!r})"   # unambiguous, ideally valid Python

    def __str__(self):
        return f"{self.value}"            # reader-friendly

__repr__ is for developers (unambiguous, recreatable); __str__ is for readers. Without __str__, print()/str() fall back to __repr__ — never the reverse.

3.__eq__ and __ne__

__eq__(self, other) defines what == does; without it, Python falls back to comparing identity (is). __ne__ is derived automatically from __eq__'s result — Python negates it, so __ne__ almost never needs defining separately.

Key features

  • Without __eq__, == falls back to is — comparing identity, not value
  • __eq__(self, other) should return True/False, or NotImplemented for an unrelated type
  • __ne__ is derived automatically from __eq__ — Python negates its result
  • Defining __eq__ sets __hash__ to None automatically, making instances unhashable
  • Returning NotImplemented (not False) lets Python try other.__eq__(self) as a fallback

Default identity comparison vs. a custom __eq__

Default identity comparison vs. a custom __eq__
Situation== result
No __eq__ definedTrue only if it is the exact same object (is)
__eq__ defined, comparing matching valuesTrue — even for two separate objects
__eq__ defined, comparing an unrelated typeshould return NotImplemented, not False directly
!= with __eq__ but no __ne__automatically the negation of __eq__'s result
== goes through __eq__ if it exists

a == b

calls a.__eq__(b)

custom __eq__

compares values, not identity

a != b

automatically negates __eq__'s result

  1. a == b — calls a.__eq__(b)
  2. custom __eq__ — compares values, not identity
  3. a != b — automatically negates __eq__'s result

Command

python
class Name:
    def __eq__(self, other):
        if not isinstance(other, Name):
            return NotImplemented
        return self.value == other.value   # compare by value, not identity

Defining __eq__ replaces identity comparison with value comparison, but sets __hash__ to None — define __hash__ too if instances need to go in a set or dict.

4.__lt__ and __le__

__lt__(self, other) defines <; __le__(self, other) defines <=. Unlike __eq__/__ne__, Python does not derive one from the other — each needs its own method, or functools.total_ordering can fill in the rest.

Key features

  • __lt__(self, other) defines self < other; __le__(self, other) defines self <= other
  • Neither is derived from the other, or from __eq__ — each must be defined explicitly
  • Should return NotImplemented for an unrelated type, the same convention __eq__ uses
  • @functools.total_ordering fills in the rest from __eq__ plus just one ordering method
  • Comparing to an unrelated type with no ordering defined raises TypeError

What has to be defined for full ordering support

What has to be defined for full ordering support
ApproachWhat is needed
Manual, all four operators__lt__, __le__, __gt__, __ge__ defined individually
@functools.total_ordering__eq__ plus just one of the four — the rest derived
Only __lt__ defined< works; <=, >, >= all raise TypeError
No ordering methods at allevery comparison operator raises TypeError

Command

python
class Name:
    def __lt__(self, other):
        if not isinstance(other, Name):
            return NotImplemented
        return self.value < other.value

    def __le__(self, other):
        if not isinstance(other, Name):
            return NotImplemented
        return self.value <= other.value

__lt__ and __le__ are independent — Python derives neither from the other or from __eq__. @functools.total_ordering fills in all four from __eq__ plus one.

5.__gt__ and __ge__

__gt__(self, other) defines >; __ge__(self, other) defines >=. Like __lt__/__le__, neither is derived from the other. If a.__gt__(b) returns NotImplemented, Python tries b.__lt__(a) as a fallback before raising TypeError.

Key features

  • __gt__(self, other) defines self > other; __ge__(self, other) defines self >= other
  • Neither is derived from __lt__/__le__ or from each other — each needs its own definition
  • If a.__gt__(b) returns NotImplemented, Python tries b.__lt__(a) as a reflected fallback
  • That reflected-fallback trick is the one piece of automatic help ordering operators get
  • @functools.total_ordering remains the simplest way to get all four from __eq__ plus one

What Python tries, and in what order

What Python tries, and in what order
ExpressionPython's attempt order
a > ba.__gt__(b), then b.__lt__(a) if the first returns NotImplemented
a >= ba.__ge__(b), then b.__le__(a) if the first returns NotImplemented
Both attempts return NotImplementedTypeError — no comparison was possible
Neither __gt__ nor __ge__ defined at allTypeError immediately — nothing to try
a > b tries a reflected fallback before giving up

a.__gt__(b)

tried first

b.__lt__(a)

tried if the first returns NotImplemented

TypeError

raised only if both attempts fail

  1. a.__gt__(b) — tried first
  2. b.__lt__(a) — tried if the first returns NotImplemented
  3. TypeError — raised only if both attempts fail

Command

python
class Name:
    def __gt__(self, other):
        if not isinstance(other, Name):
            return NotImplemented
        return self.value > other.value

    def __ge__(self, other):
        if not isinstance(other, Name):
            return NotImplemented
        return self.value >= other.value

a > b tries a.__gt__(b), then falls back to b.__lt__(a) if the first returns NotImplemented — one reflected attempt, not a full derivation of every operator.

6.__hash__

__hash__(self) returns an int used to place an object in a dict or set. The one hard rule: objects that are == must return the same hash. Defining __eq__ without __hash__ sets __hash__ to None, making instances unhashable.

Key features

  • __hash__(self) must return an int — used by dict/set to place an object into a bucket
  • Rule: a == b implies hash(a) == hash(b) — Python cannot verify this automatically
  • Defining __eq__ without __hash__ sets __hash__ to None, making instances unhashable
  • Two unequal objects CAN share a hash (a "collision") — allowed and handled internally
  • hash((self.a, self.b)) is standard — hash a tuple of the same fields __eq__ compares

What happens to __hash__, by what a class defines

What happens to __hash__, by what a class defines
Class defines__hash__ behaviour
Neither __eq__ nor __hash__default identity-based hash — every instance hashable
__eq__ only, no __hash____hash__ set to None automatically — instances become unhashable
__eq__ and __hash__ bothhashable, using whatever __hash__ returns
__hash__ returning a non-intTypeError the moment hash() is actually called

Command

python
class Name:
    def __eq__(self, other):
        return isinstance(other, Name) and self.value == other.value

    def __hash__(self):
        return hash(self.value)   # hash the same fields __eq__ compares

The rule: a == b implies hash(a) == hash(b). Defining __eq__ without __hash__ makes instances unhashable — restore it, hashing the fields __eq__ compares.

7.__bool__

__bool__(self) defines what bool(obj) and if obj: evaluate to. Without it, Python falls back to __len__ (0 means falsy); without either, every instance is truthy by default.

Key features

  • __bool__(self) must return an actual bool — True or False
  • Without __bool__, Python falls back to __len__(self) == 0 meaning falsy
  • Without either __bool__ or __len__, every instance is truthy
  • bool(obj) and if obj: both go through this same lookup chain
  • __bool__ returning a non-bool value raises TypeError

What Python checks, in order, for truthiness

What Python checks, in order, for truthiness
Defined on the classif obj: behaviour
__bool__ definedcalls it directly — whatever it returns
No __bool__, but __len__ definedfalsy only if len(obj) == 0
Neither definedalways truthy, no exceptions
__bool__ returns something other than True/FalseTypeError

Command

python
class Name:
    def __bool__(self):
        return self.some_condition   # must return True/False

__bool__ takes priority over __len__ for truthiness — without either, every instance is truthy. Reach for __bool__ when truthiness is not a "length" question.

8.__len__ and __contains__

__len__(self) defines what len(obj) returns, and must return a non-negative int. __contains__(self, item) defines what item in obj checks. Without __contains__, in falls back to iterating with __iter__ and comparing each element.

Key features

  • __len__(self) must return a non-negative int — len(obj) calls it directly
  • __contains__(self, item) defines in — should return True/False
  • Without __contains__, in falls back to iterating via __iter__ and comparing each element
  • len() raises TypeError immediately if __len__ returns a negative number
  • A class with neither __len__ nor __iter__ cannot support in at all

What in actually does, depending on what is defined

What in actually does, depending on what is defined
Class definesitem in obj behaviour
__contains__calls it directly — fastest, most explicit path
No __contains__, but __iter__falls back to iterating and comparing each element with ==
Neither __contains__ nor __iter__TypeError — not a container Python knows how to search
__len__ alone (no __contains__/__iter__)len(obj) still works; in still raises TypeError
Two separate questions about the same container

__len__

len(obj) — how many

__contains__

item in obj — is this one present

both defined

obj behaves like a real container

  1. __len__ — len(obj) — how many
  2. __contains__ — item in obj — is this one present
  3. both defined — obj behaves like a real container

Command

python
class Name:
    def __len__(self):
        return len(self._items)

    def __contains__(self, item):
        return item in self._items

__contains__ is checked before falling back to a linear __iter__ scan for "in" — define it whenever a faster membership check is available.

9.__iter__ and __next__

__iter__(self) makes an object iterable — for x in obj: calls it once for an iterator. __next__(self) makes an object an iterator, returning the next value each call and raising StopIteration when exhausted.

Key features

  • __iter__(self) must return an iterator — an object with its own __next__
  • __next__(self) returns the next value, or raises StopIteration when exhausted
  • A class implementing both on itself, returning self from __iter__, is its own iterator
  • for x in obj: is exactly iter(obj) once, then next() in a loop, stopping on StopIteration
  • A fresh call to __iter__ should start a NEW independent iteration, not resume an old one

Iterable vs. iterator — two related but different roles

Iterable vs. iterator — two related but different roles
RoleDefines
Iterable__iter__, returning an iterator (often not itself)
Iterator__next__, returning one value per call, raising StopIteration when done
Self-iterator (common)both — __iter__ returns self, __next__ advances state on self
for x in obj:calls iter(obj) once, then next() repeatedly until StopIteration
A for loop is exactly iter() once, then next() repeatedly

iter(obj)

called once, returns an iterator

next(iterator)

called repeatedly, one value per call

StopIteration

raised when exhausted — the loop ends cleanly

  1. iter(obj) — called once, returns an iterator
  2. next(iterator) — called repeatedly, one value per call
  3. StopIteration — raised when exhausted — the loop ends cleanly

Command

python
class Name:
    def __iter__(self):
        return self          # this object is also its own iterator

    def __next__(self):
        if done:
            raise StopIteration
        return next_value

__iter__ returns an iterator (often self); __next__ returns one value or raises StopIteration. Returning self means the object can only be fully iterated once.

10.__getitem__ and __setitem__

__getitem__(self, key) defines what obj[key] returns; __setitem__(self, key, value) defines what obj[key] = value does. key can be an int, a string, or a slice object — the method decides what to do with whatever it receives.

Key features

  • __getitem__(self, key) defines obj[key] — key is often an int, str, or slice
  • __setitem__(self, key, value) defines obj[key] = value
  • Without __setitem__, a class with only __getitem__ supports reads but not writes
  • A missing key should raise KeyError (mapping-style) or IndexError (sequence-style)
  • __getitem__ over sequential integer indices is auto-iterable, even without __iter__

Read vs. write, and what a missing key should raise

Read vs. write, and what a missing key should raise
SituationBehaviour
obj[key] (read)calls __getitem__(self, key)
obj[key] = value (write)calls __setitem__(self, key, value) — requires it to be defined
Sequence-style, bad indexconvention: raise IndexError
Mapping-style, missing keyconvention: raise KeyError
obj[key] and obj[key] = value are two separate hooks

obj[key]

__getitem__ — read

obj[key] = value

__setitem__ — write

both defined

obj behaves like a real mutable container

  1. obj[key] — __getitem__ — read
  2. obj[key] = value — __setitem__ — write
  3. both defined — obj behaves like a real mutable container

Command

python
class Name:
    def __getitem__(self, key):
        return self._data[key]

    def __setitem__(self, key, value):
        self._data[key] = value

__getitem__ and __setitem__ are independent — a class can be read-only. Raise IndexError (sequence-style) or KeyError (mapping-style) for a bad key.

11.__enter__ and __exit__

__enter__(self) runs at the top of a with block and its return value becomes the as name. __exit__(self, exc_type, exc_value, traceback) always runs when the block ends — even if it raised — and can suppress the exception by returning True.

Key features

  • __enter__(self) runs at the start of a with block; its return value becomes as name
  • __exit__(self, exc_type, exc_value, traceback) always runs at the end, even on error
  • If no exception occurred, all three __exit__ arguments are None
  • Returning True from __exit__ suppresses the exception — as if nothing happened
  • Returning falsy (the default) lets the exception propagate after __exit__ runs

What __exit__ receives, and what its return value means

What __exit__ receives, and what its return value means
SituationBehaviour
Block completes normallyexc_type/exc_value/traceback are all None
Block raises an exceptionexc_type/exc_value/traceback describe it
__exit__ returns Truethe exception is suppressed — swallowed, not re-raised
__exit__ returns None/False (default)the exception propagates normally after __exit__ finishes
__exit__ runs no matter how the block ends

__enter__

setup — runs first

with block body

may complete, or raise

__exit__

cleanup — always runs

  1. __enter__ — setup — runs first
  2. with block body — may complete, or raise
  3. __exit__ — cleanup — always runs

Command

python
class Name:
    def __enter__(self):
        return self                       # becomes the "as" name

    def __exit__(self, exc_type, exc_value, traceback):
        # cleanup here — always runs
        return False                      # do not suppress exceptions

__exit__ always runs, even on an exception — return a truthy value to deliberately suppress it, or False (the safe default) to let it propagate normally.

12.__call__

__call__(self, *args, **kwargs) lets an instance be called like a function — obj(1, 2) runs obj.__call__(1, 2). Common for objects that need to remember state between calls, like a running counter.

Key features

  • __call__(self, *args, **kwargs) lets obj(...) work — sugar for obj.__call__(...)
  • callable(obj) returns True for any object whose class defines __call__
  • A class implementing __call__ can hold state between calls, unlike a plain function
  • Functions themselves are callable because function objects implement __call__ too
  • Common uses: configurable operations, decorators-as-classes, running counters/accumulators

Plain function vs. a callable object, for the same job

Plain function vs. a callable object, for the same job
ApproachState between calls
A plain functionnone — every call starts fresh unless using a closure or global
A class with __call__self.anything persists naturally between calls
callable(obj)True whenever obj's class defines __call__
obj(args)exact sugar for obj.__call__(args)
obj(args) is sugar for obj.__call__(args)

obj(args)

looks like calling a function

__call__(self, args)

runs, with access to self

state persists

self.anything survives to the next call

  1. obj(args) — looks like calling a function
  2. __call__(self, args) — runs, with access to self
  3. state persists — self.anything survives to the next call

Command

python
class Name:
    def __call__(self, *args, **kwargs):
        return self.do_something(*args, **kwargs)

__call__ makes obj(args) work as sugar for obj.__call__(args) — reach for it when an object needs to hold and mutate state across separate calls.

Remember

  • __new__ creates the object, __init__ sets it up — Name(...) is always both steps, in that order.
  • Defining __eq__ sets __hash__ to None automatically — restore it explicitly to keep instances usable in a set or dict.
  • Each ordering operator (__lt__, __le__, __gt__, __ge__) is independent — none is derived from another, unlike __ne__ from __eq__.
  • __exit__ always runs, even on an exception — return a truthy value to deliberately suppress it, False to let it propagate.

Pro tip

  • Define __repr__ on nearly every class — it is what shows inside a list/dict even when __str__ is also defined.
  • @functools.total_ordering fills in all four ordering operators from __eq__ plus just one — far less than defining all four by hand.
  • Returning NotImplemented (not False) from a comparison dunder lets Python try the other operand's reflected method first.
  • __call__ earns its complexity when an object needs to hold and mutate state across calls — a closure is simpler otherwise.

Next up

  • Advanced OOP

Sources

  • Data model — docs.python.org/3/reference/datamodel.html
  • Every example executed on Python 3.14.3 · checked 2026-08-21

Spotted a mistake on this page? Tell us.