Your own registry
dispatch is a ready-made registry that keeps
functions separate per module, so the same name defined in two different
modules produces two independent functions. A
Dispatcher built directly behaves
differently: it keys a function by its qualified name alone, so every
module that registers that name into the same Dispatcher instance extends
a single, shared function. Build one when several modules need to
contribute overloads to what is conceptually one generic function.
Naming a function before it exists
A function held by a Dispatcher is reached through its functions
namespace, which get-or-creates: naming a function that does not exist yet
creates it empty, so a registry and the modules that register onto it can
name the same function in either order:
>>> from bagof.dispatchers import Dispatcher
>>> registry = Dispatcher()
>>> render = registry.functions.render # an empty function, for now
>>> @registry
... def render(x: int) -> str:
... return "int:%d" % x
>>> registry.functions["render"] is render is registry.functions.render
True
>>> render(7)
'int:7'
Names that would otherwise collide
The functions namespace exposes no methods of its own, so a function may
be given any name, including one that would otherwise collide with a
namespace method, such as register, items, or map:
>>> registry = Dispatcher()
>>> items = registry.functions["items"]
>>> @items.register
... def _(x: int) -> str:
... return "one item"
>>> registry.functions.items(0)
'one item'
>>> "items" in registry.functions
True
Attribute access ignores names beginning with an underscore, so a REPL or a
tool probing for dunder attributes never mints an empty function by
accident. Item access does not apply that rule, so
registry.functions["_x"] still names a function whose name starts with an
underscore.
This distinction matters for anonymous overloads too. Registering
@dispatch def _ or a lambda directly keys the function by the literal
name "_" or "<lambda>". Several such registrations on the same registry
therefore collapse into a single function instead of staying independent.
Give each overload a real name, or overlay hints onto a named def, to
avoid the collision.