Best way to "overload" function in python?

2.4k views Asked by At

I am trying to do something like this in python:

def foo(x, y):
    # do something at position (x, y)

def foo(pos):
    foo(pos.x, pos.y)

So I want to call a different version of foo depending on the number of parameters I provide. This of course doesn't work because I redefined foo.

What would be the most elegant way of achieving this? I would prefer not to use named parameters.

5

There are 5 answers

0
Steve Jessop On BEST ANSWER

Usually you'd either define two different functions, or do something like:

def foo(x, y = None):
    if y is None:
        x, y = x.x, x.y
    # do something at position (x, y)

The option to define two different functions seems unwieldy if you're used to languages that have overloading, but if you're used to languages like Python or C that don't, then it's just what you do. The main problem with the above code in Python is that it's a bit awkward to document the first parameter, it doesn't mean the same in the two cases.

Another option is to only define the version of foo that takes a pos, but also supply a type for users:

Pos = collections.namedtuple('Pos', 'x y')

Then anyone who would have written foo(x,y) can instead write foo(Pos(x,y)). Naturally there's a slight performance cost, since an object has to be created.

0
Maroun On

One way by setting a default value:

def foo(x, y=None):    
    # now for example, you can call foo(1) and foo(1,2)     

Inside foo you can check if y == None and have a different logic for the two cases.

Note that you can have a better design if you separate the functions, don't attempt to have the same function that accept both coordinates and position.

2
matino On

What you're trying to achieve looks like a bad design, maybe consider sticking with different function names:

def do_something_by_coordinates(x, y):
    pass

def do_something_by_position(pos):
    do_something_by_coordinates(pos.x, pos.y)

Or you can use kwargs if you really need to:
Understanding kwargs in Python

4
szarad On

You can use default parameters, but if you want use overloading in python, the best way to do this is:

from pytyp.spec.dispatch import overload

@overload
def foo(x,y):

@foo.overload
def foo(x, y,z):

Pytyp is a python package (you can download it from https://pypi.python.org/pypi/pytyp). It includes module pytyp.spec.dispatch, which contains decorators, like overload. If it is palced on method, this method will be called for all the overloaded methods..

2
stderr On

There are a lot of things you could do, like named default parameters.

However, what it looks like you want is "multiple dispatch" and there's a number of implementations out of there of decorators to help you do that sort of thing. Here's one implementation that looks like:

>>> from multipledispatch import dispatch

>>> @dispatch(int, int)
... def add(x, y):
...     return x + y

>>> @dispatch(Position)
... def add(pos):
...     return "%s + %s" % (pos.x, pos.y)

>>> add(1, 2)
3

>>> pos = Position(3, 4)
>>> add(pos)
7

While @functools.singledispatch is coming to Python 3.4 I don't think that will work for your example as in one case you have multiple arguments.