Today I spent some time exploring Python protocols.
At first they felt abstract, but then I realized they are simply special methods that let Python know how an object should behave.
I wrote these notes so I can remember them later.
__len__ → enables len(obj)
__getitem__ → enables indexing like obj[i] and slicing obj[1:3]
__contains__ → enables membership checks like x in obj
__iter__ → allows an object to work with for loops, list(), sum()
__next__ → enables step-by-step iteration inside iterators
__repr__ → developer representation (used in REPL / debugging)
__str__ → user friendly representation (used by print())
__add__ → enables obj + other
__mul__ → enables obj * other
__eq__ → enables obj == other
__lt__ → enables obj < other
functools.total_ordering → helps implement all comparison operators easily
__getattr__ → called when an attribute is missing
__getattribute__ → intercepts all attribute access
__setattr__ → intercepts attribute assignment
__get__ → descriptor protocol (computed attributes)
__set__ → descriptor assignment logic
__new__ → object creation (used in singletons / immutables)
__init__ → object initialization
__call__ → makes an object callable like obj()
__hash__ → allows object to be used in sets and dictionary keys
__missing__ → custom behavior for missing dictionary keys
__slots__ → memory optimization (removes instance __dict__)
What I like about protocols is that they make Python objects feel natural.
If an object defines __len__, Python knows how to use len().
If it defines __iter__, it works with for loops.
Instead of memorizing rules, it feels like learning how Python communicates with objects.
Still learning.