Python 3.4 added the ability to define function overloading with static methods. This is essentially the example from the documentation:
from functools import singledispatch
class TestClass(object):
@singledispatch
def test_method(arg, verbose=False):
if verbose:
print("Let me just say,", end=" ")
print(arg)
@test_method.register(int)
def _(arg):
print("Strength in numbers, eh?", end=" ")
print(arg)
@test_method.register(list)
def _(arg):
print("Enumerate this:")
for i, elem in enumerate(arg):
print(i, elem)
if __name__ == '__main__':
TestClass.test_method(55555)
TestClass.test_method([33, 22, 11])
In its purest form, the singledispatch
implementation relies on the first argument to identify type, therefore making it tricky to extend this functionality to instance methods.
Does anyone have any advice for how to use (or jerry-rig) this functionality to get it to work with instance methods?
Looking at the source for
singledispatch
, we can see that the decorator returns a functionwrapper()
, which selects a function to call from those registered based on the type ofargs[0]
...... which is fine for a regular function, but not much use for an instance method, whose first argument is always going to be
self
.We can, however, write a new decorator
methdispatch
, which relies onsingledispatch
to do the heavy lifting, but instead returns a wrapper function that selects which registered function to call based on the type ofargs[1]
:Here's a simple example of the decorator in use:
Notice that both the decorated
get()
method and the method registered tolist
have an initialself
argument as usual.Testing the
Patchwork
class:A decorator is essentially a wrapper that takes the wrapped function as an argument and returns another function.
As stated in the accepted answer,
singledispatch
returns awrapper
that takes the first argument as registered type -self
in instance methods.As shown in that answer, in cases like this you can write another wrapper to monkey patch the decorator. But this kind of hacky fixes are not always the best option.
As with like any other function, you can call the wrapper and pass the arguments to it explicitly, which seems simpler, flatter and more readable to me if this kind of method overloading is only seldom made in a package.
There's another module,
multipledispatch
(not standard but included in Anaconda and without any non-standard dependencies) that, as the name already indicates and unlikesingledispatch
, allows multimethods.In addition to
Dispatcher
objects, withsingledispatch
-compatible syntaxis, it provides adispatch
decorator which hides the creation and manipulation of these objects from the user.For instance: