Dunder methods are hooks the data model calls for syntax and built-ins
Dunder methods (double underscore before and after) are the hooks Python's data model invokes when you use syntax or built-ins. len(x) calls type(x).len, x + y calls add, x[i] calls getitem, str(x) calls str, and with calls enter/exit. The interpreter looks these up on the type, not the instance, for most operators, which is a performance optimization and prevents accidental shadowing. Operator overloading in Python is therefore just implementing the right dunder with the correct return convention. The reflected methods (radd, rsub, etc.) handle the case where the left operand does not implement the operation.
Implement add, sub, mul for arithmetic; implement radd and friends for reflected operands.
Implement eq, lt, hash for comparisons and hashability. Use functools.total_ordering to generate the rest.
Implement len, getitem, iter for sequence-like behavior.
Trade-off: overloading makes code expressive but can surprise readers if semantics are unintuitive. Follow the principle of least surprise.
Common mistake: implementing eq without hash, which makes the class unhashable by setting hash to None.
Common mistake: assuming dunder lookup falls back to the instance. For implicit invocations it does not, except for a small set of special cases.
Version note: the set of dunders has grown over time, e.g., matmul in 3.5, class_getitem in 3.7, and init_subclass in 3.6.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience