Skip to main content

Documentation Index

Fetch the complete documentation index at: https://edgepython.com/llms.txt

Use this file to discover all available pages before exploring further.

Dunders (__add__, __eq__, __getitem__, …) plug a class into language protocols. Define them in the class body; the VM calls them when the corresponding operator, builtin, or syntax form runs.
class V:
    def __init__(self, n):
        self.n = n
    def __add__(self, o):
        return V(self.n + o.n)
    def __eq__(self, o):
        return self.n == o.n

print((V(3) + V(4)).n)
print(V(3) == V(3))
Output
7
True
Dunders are looked up on the class chain (instance dict skipped). Subclasses inherit and may override, operator overloading composes with single-level inheritance. Monomorphic sites (same class for both operands) promote through the IC after 4 hits and bypass lookup entirely.

Arithmetic

OperatorForwardReflected
a + b__add____radd__
a - b__sub____rsub__
a * b__mul____rmul__
a / b__truediv____rtruediv__
a // b__floordiv____rfloordiv__
a % b__mod____rmod__
a ** b__pow____rpow__
-a__neg__,
Returning NotImplemented from the forward op tells the VM to try the reflected op on the other operand. Both NotImplemented (or neither defined) -> TypeError. Subclass-first: when type(b) is a strict subclass of type(a), b.__radd__ runs before a.__add__, lets a subclass override an inherited reflected op without touching the base.
class Money:
    def __init__(self, n): self.n = n
    def __add__(self, o):
        return Money(self.n + (o.n if isinstance(o, Money) else o))
    def __radd__(self, o):
        return Money(o + self.n)

print((Money(10) + Money(5)).n)
print((3 + Money(7)).n)
Output
15
10

Comparison

OperatorForwardReflected
a == b__eq____eq__
a != b__eq____eq__
a < b__lt____gt__
a <= b__le____ge__
a > b__gt____lt__
a >= b__ge____le__
!= falls back to not __eq__ when __ne__ is absent. Results coerce to bool, __lt__ returning 'A.lt' yields True, not the string.

Truth and length

bool(x) (and any boolean context) consults:
  1. __bool__ if defined -> cast to bool.
  2. __len__ if defined -> False when length is 0, else True.
  3. Default True.
len(x) calls __len__ directly; must return a non-negative int.
class Empty:
    def __bool__(self):
        return False

class Container:
    def __init__(self, n): self.n = n
    def __len__(self):
        return self.n

print(bool(Empty()))
print(bool(Container(0)), bool(Container(3)))
print(len(Container(5)))
Output
False
False True
5

Indexing and containment

FormDunderArguments
obj[i]__getitem__(self, i)
obj[i] = v__setitem__(self, i, value)
del obj[i]__delitem__(self, i)
v in obj__contains__(self, value)
Slices pass as a slice object: obj[1:3] calls __getitem__(self, slice(1, 3, None)). Absent __contains__: v in obj falls back to iterating obj with __eq__.

Iteration

MethodRole
__iter__Returns an iterator (often self).
__next__Returns the next item, or raises StopIteration to end the loop.
class Up:
    def __init__(self, stop):
        self.i = 0
        self.stop = stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.i >= self.stop:
            raise StopIteration
        self.i += 1
        return self.i

print(list(Up(3)))
Output
[1, 2, 3]
for loops, list(x), and tuple(x) all honour the protocol.

Callable

__call__ makes instances invocable.
class Double:
    def __call__(self, x):
        return x * 2

d = Double()
print(d(7))
print(callable(d))
Output
14
True

Hashing

hash(x) calls __hash__; must return int (masked to INT_MAX). Eq/hash invariant: a class defining __eq__ without __hash__ is unhashable, hash(x) and {x: 1} raise TypeError. Prevents inconsistent dict keys.
class K:
    def __init__(self, n): self.n = n
    def __hash__(self):
        return self.n
    def __eq__(self, o):
        return self.n == o.n

print(hash(K(5)))
print({K(1): 'one'}[K(1)] if K(1).__hash__() == K(1).__hash__() else 'unhashable')
Output
5
one
Built-in dict/set still compare instance keys by identity (Val bits); user __hash__ is returned by hash() but doesn’t change containment in built-in containers. Use the same instance reference to look up reliably.

Representation

Function / formDunderFallback
repr(x)__repr__<ClassName instance>
str(x), print(x)__str____repr__, then default
f"{x}" (no spec)__str__same as str(x)
f"{x:spec}"__format__built-in format spec engine
f"{x!r}"__repr__,
__format__(spec) receives the spec string and must return str.

Attribute access fallback

__getattr__(self, name) runs only when normal lookup (instance dict -> class chain) misses. Receives the name as a string; returns the value or raises AttributeError to surface a real miss.
class Proxy:
    def __getattr__(self, name):
        return f"computed:{name}"

p = Proxy()
print(p.anything)
print(p.foo)
Output
computed:anything
computed:foo
Existing attributes bypass __getattr__; only misses trigger it.

Context managers

with cm() as x: invokes __enter__; its return binds to as. On exit, __exit__(exc_type, exc_value, traceback) runs, (None, None, None) for normal exit, live exception info on raise. Truthy return suppresses; falsy propagates.
class Suppress:
    def __enter__(self):
        return self
    def __exit__(self, t, v, tb):
        return True # swallow whatever raised

with Suppress():
    raise ValueError("boom")
print("after")
Output
after
Multiple managers (with a(), b() as x:) nest LIFO, b enters last, exits first. Each has its own implicit handler, so inner suppression still lets outer managers run their normal __exit__(None, None, None). If __exit__ itself raises, the new exception replaces the original.

What’s not dispatched

Parsed for compatibility but never invoked on user classes:
  • __init_subclass__, __set_name__, descriptors (__get__ / __set__ / __delete__)
  • __new__, VM constructs the instance; __init__ runs user logic
  • Augmented-assignment dunders (__iadd__, …), a += b desugars to a = a + b, so __add__ covers it
  • Async dunders (__aenter__ / __aexit__ / __aiter__ / __anext__), async with / async for use the sync paths
For class basics (constructors, inheritance, properties), see Classes.