Method overloading for different argument type in python
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: City Beneath the Waves Looping
--
Chapters
00:00 Method Overloading For Different Argument Type In Python
01:26 Accepted Answer Score 20
01:55 Answer 2 Score 15
03:07 Answer 3 Score 22
03:49 Answer 4 Score 3
04:14 Thank you
--
Full question
https://stackoverflow.com/questions/2534...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #oop #designpatterns
#avk47
ANSWER 1
Score 22
If you're using Python 3.4 (or are willing to install the backport for Python 2.6+), you can use functools.singledispatch for this*:
from functools import singledispatch
class S_Block(object): pass
class S_Empty(object): pass
class S_Function(object): pass
class Test(object):
def __init__(self):
self.render = singledispatch(self.render)
self.render.register(S_Block, self._render_block)
self.render.register(S_Empty, self._render_empty)
self.render.register(S_Function, self._render_function)
def render(self, s):
raise TypeError("This type isn't supported: {}".format(type(s)))
def _render_block(self, s):
print("render block")
def _render_empty(self, s):
print("render empty")
def _render_function(self, s):
print("render function")
if __name__ == "__main__":
t = Test()
b = S_Block()
f = S_Function()
e = S_Empty()
t.render(b)
t.render(f)
t.render(e)
Output:
render block
render function
render empty
*Code based on this gist.
ACCEPTED ANSWER
Score 20
Would something like this work?
self.map = {
S_Block : self._render_block,
S_Empty : self._render_empty,
S_Function: self._render_function
}
def render(self, s):
return self.map[type(s)](s)
Keeping a reference to a class object as a key in a dictionary and having it's value be the function object you want to call will make your code shorter and less error prone. The only place an error could occur here would be in the definition of the dictionary. Or one of your internal functions of course.
ANSWER 3
Score 15
The overloading syntax you are looking for can be achieved using Guido van Rossum's multimethod decorator.
Here is a variant of the multimethod decorator which can decorate class methods (the original decorates plain functions). I've named the variant multidispatch to disambiguate it from the original:
import functools
def multidispatch(*types):
def register(function):
name = function.__name__
mm = multidispatch.registry.get(name)
if mm is None:
@functools.wraps(function)
def wrapper(self, *args):
types = tuple(arg.__class__ for arg in args)
function = wrapper.typemap.get(types)
if function is None:
raise TypeError("no match")
return function(self, *args)
wrapper.typemap = {}
mm = multidispatch.registry[name] = wrapper
if types in mm.typemap:
raise TypeError("duplicate registration")
mm.typemap[types] = function
return mm
return register
multidispatch.registry = {}
and it can be used like this:
class Foo(object):
@multidispatch(str)
def render(self, s):
print('string: {}'.format(s))
@multidispatch(float)
def render(self, s):
print('float: {}'.format(s))
@multidispatch(float, int)
def render(self, s, t):
print('float, int: {}, {}'.format(s, t))
foo = Foo()
foo.render('text')
# string: text
foo.render(1.234)
# float: 1.234
foo.render(1.234, 2)
# float, int: 1.234, 2
The demo code above shows how to overload the Foo.render method based on the types of its arguments.
This code searches for exact matching types as opposed to checking for isinstance relationships. It could be modified to handle that (at the expense of making the lookups O(n) instead of O(1)) but since it sounds like you don't need this anyway, I'll leave the code in this simpler form.
ANSWER 4
Score 3
To add some performance measurements to the @unutbu 's answer:
@multimethod(float)
def foo(bar: float) -> str:
return 'float: {}'.format(bar)
def foo_simple(bar):
return 'string: {}'.format(bar)
import time
string_type = "test"
iterations = 10000000
start_time1 = time.time()
for i in range(iterations):
foo(string_type)
end_time1 = time.time() - start_time1
start_time2 = time.time()
for i in range(iterations):
foo_simple(string_type)
end_time2 = time.time() - start_time2
print("multimethod: " + str(end_time1))
print("standard: " + str(end_time2))
Returns:
> multimethod: 16.846999883651733
> standard: 4.509999990463257