Update: As of Python 3.8,
functools.singledispatchmethod
allows single dispatch on methods, classmethods, abstractmethods,
and staticmethods.For older Python versions, see the rest of this answer.
Looking at the source for singledispatch
, we can see that the decorator returns a function wrapper()
, which selects a function to call from those registered based on the type of args[0]
…
def wrapper(*args, **kw):
return dispatch(args[0].__class__)(*args, **kw)
… 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 on singledispatch
to do the heavy lifting, but instead returns a wrapper function that selects which registered function to call based on the type of args[1]
:
from functools import singledispatch, update_wrapper
def methdispatch(func):
dispatcher = singledispatch(func)
def wrapper(*args, **kw):
return dispatcher.dispatch(args[1].__class__)(*args, **kw)
wrapper.register = dispatcher.register
update_wrapper(wrapper, func)
return wrapper
Here’s a simple example of the decorator in use:
class Patchwork(object):
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
@methdispatch
def get(self, arg):
return getattr(self, arg, None)
@get.register(list)
def _(self, arg):
return [self.get(x) for x in arg]
Notice that both the decorated get()
method and the method registered to list
have an initial self
argument as usual.
Testing the Patchwork
class:
>>> pw = Patchwork(a=1, b=2, c=3)
>>> pw.get("b")
2
>>> pw.get(["a", "c"])
[1, 3]