Method overloading for different argument type in python
Method overloading for different argument type in python
--
Become part of the top 3% of the developers by applying to Toptal
https://topt.al/25cXVn
--
Music by Eric Matyas
https://www.soundimage.org
Track title: Over a Mysterious Island
--
Chapters
00:00 Question
02:02 Accepted answer (Score 19)
02:37 Answer 2 (Score 20)
03:40 Answer 3 (Score 14)
05:16 Answer 4 (Score 4)
07:33 Thank you
--
Full question
https://stackoverflow.com/questions/2534...
Question links:
[interesting "visitor" code]: http://curtis.schlak.com/2013/06/20/foll...
Answer 1 links:
[backport]: https://pypi.python.org/pypi/singledispa...
[functools.singledispatch]: https://docs.python.org/3/library/functo...
[this gist.]: https://gist.github.com/ambv/5682351
Answer 2 links:
[Guido van Rossum's multimethod decorator]: http://www.artima.com/weblogs/viewpost.j...
Answer 3 links:
[functools.singledispatch]: https://docs.python.org/3/library/functo...
[PEP-443]: https://www.python.org/dev/peps/pep-0443/
[another answer]: https://stackoverflow.com/a/25344445/389...
[backport]: https://pypi.org/project/singledispatch/
[source code]: https://github.com/python/cpython/blob/4...
[this answer]: https://stackoverflow.com/a/24602374/389...
--
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