The simplegeneric module lets you define simple single-dispatch
generic functions, akin to Python’s built-in generic functions like
len(), iter() and so on. However, instead of using
specially-named methods, these generic functions use simple lookup
tables, akin to those used by e.g. pickle.dump() and other
generic functions found in the Python standard library.

As you can see from the above examples, generic functions are actually
quite common in Python already, but there is no standard way to create
simple ones. This library attempts to fill that gap, as generic
functions are an excellent alternative to the Visitor pattern, as
well as being a great substitute for most common uses of adaptation.

This library tries to be the simplest possible implementation of generic
functions, and it therefore eschews the use of multiple or predicate
dispatch, as well as avoiding speedup techniques such as C dispatching
or code generation. But it has absolutely no dependencies, other than
Python 2.4, and the implementation is just a single Python module of
less than 100 lines.

Defaults and Docs

You can obtain a function’s default implementation using its default
attribute:

>>> @move.when_type(Y)
... def move_y(item, target):
... print("Someone set us up the %s!!!" % (target,))
... move.default(item, target)
>>> move(Y(), "dance")
Someone set us up the dance!!!
what you say?!

help() and other documentation tools see generic functions as normal
function objects, with the same name, attributes, docstring, and module as
the prototype/default function:

Any methods added to the new generic function override all methods in the
“base” function:

>>> @move2.when_type(X)
... def move2_X(item, target):
... print("You have no chance to survive make your %s!" % (target,))
>>> move2(X(), "time")
You have no chance to survive make your time!
>>> move2(Y(), "time")
You have no chance to survive make your time!

Notice that even though move() has a method for type Y, the method
defined for X in move2() takes precedence. This is because the
move function is used as the default method of move2, and move2
has no method for type Y:

Limitations

The first argument is always used for dispatching, and it must always be
passed positionally when the function is called.

Documentation tools don’t see the function’s original argument signature, so
you have to describe it in the docstring.

If you have optional arguments, you must duplicate them on every method in
order for them to work correctly. (On the plus side, it means you can have
different defaults or required arguments for each method, although relying on
that quirk probably isn’t a good idea.)

These restrictions may be lifted in later releases, if I feel the need. They
would require runtime code generation the way I do it in RuleDispatch,
however, which is somewhat of a pain. (Alternately I could use the
BytecodeAssembler package to do the code generation, as that’s a lot easier
to use than string-based code generation, but that would introduce more
dependencies, and I’m trying to keep this simple so I can just
toss it into Chandler without a big footprint increase.)