Reputation: 28036
I'm making the transition over to Python3 and have been exploring some of the functionality of the stdlib. functools.singledispatch caught my eye and I've been playing around with it a little bit. However, at the point where I tried using it in a class I ran into some problems.
It doesn't appear to work with functions registered inside the class, you can make it work by directly calling fun.dispatch(type(arg))(argname=arg) and I was wondering if there was a better way to do it.
I tried using @classmethod and @staticmethod as decorators above and below the registration but that didn't work.
Here's a contrived example that registers handlers to convert the input argument when creating a class to ensure that it will always be a list.
from functools import singledispatch
class UrlDispatcher(object):
@singledispatch
def url_input(self, input):
print('input wasn\'t dispatched', input)
@url_input.register(str)
def _(self, input):
print('input is a str', input)
self.input = [input]
@url_input.register(list)
def _(self, input):
print('input is a list', input)
self.input = input
def __init__(self, arg):
# Works, albeit clunkily
self.url_input.dispatch(type(arg))(self,input=arg)
# Always uses the base dispatcher
self.url_input(input=arg)
a = "http://www.cnn.com"
b = ["http://www.google.com", "http://www.slashdot.org"]
s1 = UrlDispatcher(a)
s2 = UrlDispatcher(b)
Upvotes: 1
Views: 3171
Reputation: 91
Here is my solution, work on any Python (3.8 or less)
class MyClass:
def over_ride_func( self, arg):
if type(arg) is list:
self.over_ride_func_list (arg)
if type(arg) is int:
self.over_ride_func_int (arg)
if type(arg) is float:
self.over_ride_func_float (arg)
if type(arg) is str:
self.over_ride_func_string (arg)
def over_ride_func_list(self, arg ):
print ("List arg: ", arg, " with legnth", len(arg), " first item", arg[0])
def over_ride_func_int(self, arg ):
print ("int arg: ", arg)
def over_ride_func_float(self, arg ):
print ("Float arg: ", arg)
def over_ride_func_string(self, arg ):
print ("String arg ", arg)
obj_myclass = MyClass()
obj_myclass.over_ride_func(665)
obj_myclass.over_ride_func(444.31)
obj_myclass.over_ride_func([3,5,6])
obj_myclass.over_ride_func("Hello over ride function")
Upvotes: 1
Reputation: 1175
https://docs.python.org/3/library/functools.html#functools.singledispatchmethod
as of python 3.8 there is an stdlib function for method dispatching
Upvotes: 2
Reputation: 28036
I found the answer - you don't.
http://code.activestate.com/lists/python-dev/122554/
Quoting from a post I found at the above URL I think it's explained - short answer is 'generic functions' are for stateless algorithms. I was unaware of that definition.
Correct. OO and generic functions are different development paradigms, and there are limitations on mixing them. Generic functions are for stateless algorithms, which expect to receive all required input through their arguments. By contrast, class and instance methods expect to receive some state implicitly - in many respects, they already are generic functions.
Thus, this is really a request for dual dispatch in disguise: you want to first dispatch on the class or instance (through method dispatch) and then dispatch on the second argument (through generic function dispatch).
Dual dispatch is much harder than single dispatch and "functools.singledispatch" does not and should not support it (it's in the name). As PJE noted, you can use singledispatch with staticmethods, as that eliminates the dual dispatch behaviour by removing the class and instance based dispatch step. You can also register already bound class and instance methods as implementations for a generic function, as that also resolves the dual dispatch in a way that means the single dispatch implementation doesn't even need to be aware it is happening.
Upvotes: 1
Reputation: 281683
The following should work. Whether or not it's the best solution, I don't know.
class Foo:
def method(self, arg):
_method(arg, self)
@functools.singledispatch
def _method(arg, self):
...
...
...
Upvotes: 3