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
Python Script for Django App to Access Models Without Using Manage.Py Shell
Python Progression Path - from Apprentice to Guru
Using Print() (The Function Version) in Python2.X
How to Add Conda Environment to Jupyter Lab
Calculating Pearson Correlation and Significance in Python
Python "Extend" for a Dictionary
Python Argparse - Add Argument to Multiple Subparsers
Matplotlib Bar Chart with Dates
Python Datetime to String Without Microsecond Component
Pandas Concat Generates Nan Values
Installing Setuptools on 64-Bit Windows
Reduce Left and Right Margins in Matplotlib Plot
Access to Table Objects on Webpage Using Python Selenium
How to Simulate Jumping in Pygame for This Particular Code
Longest Common Substring from More Than Two Strings
Why Is Using Thread Locals in Django Bad
What's the Difference Between "Update" and "Update_Idletasks"