Overloaded Functions in Python

Overloaded functions in Python

EDIT For the new single dispatch generic functions in Python 3.4, see http://www.python.org/dev/peps/pep-0443/

You generally don't need to overload functions in Python. Python is dynamically typed, and supports optional arguments to functions.

def myfunction(first, second, third = None):
if third is None:
#just use first and second
else:
#use all three

myfunction(1, 2) # third will be None, so enter the 'if' clause
myfunction(3, 4, 5) # third isn't None, it's 5, so enter the 'else' clause

Function overloading in Python: Missing

As unwind noted, keyword arguments with default values can go a long way.

I'll also state that in my opinion, it goes against the spirit of Python to worry a lot about what types are passed into methods. In Python, I think it's more accepted to use duck typing -- asking what an object can do, rather than what it is.

Thus, if your method may accept a string or a tuple, you might do something like this:

def print_names(names):
"""Takes a space-delimited string or an iterable"""
try:
for name in names.split(): # string case
print name
except AttributeError:
for name in names:
print name

Then you could do either of these:

print_names("Ryan Billy")
print_names(("Ryan", "Billy"))

Although an API like that sometimes indicates a design problem.

Python function overloading recommended approach

After some research, and taking inspirations from the @Copperfield answer, I found an elegant solution to the problem.

Let's first rephrase the problem - we have a function that takes an object. We want to provide some overloads, which will do the validations/ conversions etc. and call the base function which accepts object. We also need to reject any call not following any function signature which are not implemented.

The library that I found very useful was multipledispatch. An easy example:

from multipledispatch import dispatch

@dispatch(int, int)
def add_nums(num1: int, num2: int) -> int:
return num1 + num2

@dispatch(str, str)
def add_nums(num1: str, num2: str) -> int:
# do some useful validations/ object transformations
# implement any intermediate logic before calling the base func
# this enables base function do it's intended feature rather than
# implementing overloads
return add_nums(int(num1), int(num2))

if we call add_nums(40, 15), we get 55 as the (int, int) version get called. add_nums('10', '15') get us 25 as expected as (str, str) version get called.

It becomes very interesting when we call add_nuns(10, 10.0) as this will fail saying NotImplementedError: Could not find signature for add_nums: <int, float>. Essentially any call not in (int, int) or (str, str) format, fail with NotImplementedError exception.

This is by far the closest behaviour of function overloading, when comparing with typed languages.

The only concern I have - this library was last updated on Aug 9, 2018.

python static overloading of member function

You aren't overloading the method; you are replacing it with the static method. You'll have to pick a different name for the static method.

How to type-overload functions with multiple optional args?

It is an old problem. The reason is that you specify default value in both branches, so x() is possible in both and return type is undefined.

I have the following pattern for such cases:

from typing import overload, Literal

@overload
def x(a: int = 5, t: Literal[True] = True, b: int = 5) -> int: ...

@overload
def x(a: int = 5, *, t: Literal[False], b: int = 5) -> str: ...

@overload
def x(a: int, t: Literal[False], b: int = 5) -> str: ...

def x(a: int = 5, t: bool = True, b: int = 5) -> int | str:
if t:
return 5
return "asd"

Why and how? You have to think about ways to call your function. First, you can provide a, then t can be given as kwarg (#2) or arg (#3). You can also leave a default, then t is always a kwarg (#2 again). This is needed to prevent putting arg after kwarg, which is SyntaxError. Overloading on more than one parameter is more difficult, but possible this way too:

@overload
def f(a: int = 1, b: Literal[True] = True, c: Literal[True] = True) -> int: ...

@overload
def f(a: int = 1, *, b: Literal[False], c: Literal[True] = True) -> Literal['True']: ...

@overload
def f(a: int = 1, *, b: Literal[False], c: Literal[False]) -> Literal['False']: ...

@overload
def f(a: int, b: Literal[False], c: Literal[True] = True) -> Literal['True']: ...

@overload
def f(a: int, b: Literal[False], c: Literal[False]) -> Literal['False']: ...

def f(a: int = 1, b: bool = True, c: bool = True) -> int | Literal['True', 'False']:
return a if b else ('True' if c else 'False') # mypy doesn't like str(c)

You can play with overloading here.



Related Topics



Leave a reply



Submit