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
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.
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 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.
type function overloading in python
type()
isn't using overloading. It is using the C-code equivalent of *args
to alter behaviour based on how many arguments you passed in. If you are interested in the specifics, see the type_new
function:
/* Special case: type(x) should return x->ob_type */
{
const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
if (PyType_CheckExact(metatype) && nargs == 1 && nkwds == 0) {
PyObject *x = PyTuple_GET_ITEM(args, 0);
Py_INCREF(Py_TYPE(x));
return (PyObject *) Py_TYPE(x);
}
which essentially tests if len(args) == 1 and not kwargs
, as well as making sure the first argument is the type
object itself and not a subclass.
You could do something similar with your Dog.Print()
method; if args
is not empty assume it was called with name, age
instead of just age
:
def Print(self, age, *args):
if args:
name, age = args + (age,)
else:
name = None
if name is not None:
print(name)
print(age)
although that would make it a terribly bad API. It is better to add optional arguments to the end:
def Print(self, age, name=None):
if name is not None:
print(name)
print(age)
as that reduces confusion and is easier to remember.
Pythonic function overloading?
Don't use overloading. Where the problem that you're trying to solve is "about ~80% of the code in each function is repeated", the better solution is to factor out that code into its own function, call it _set_delay
for example, then call it from each of the set_delay_*
functions.
To me, overloading only makes sense where the parameters overlap, but none do in your case. For contrast, say I write range(10)
but then I realize I want to start from 1; thanks to overloading, I can simply change it to range(1, 10)
. Meanwhile, if I added an argument to your proposed set_delay
, I would get totally different behaviour.
Related Topics
Use Df Command to Show Only the %Used
Slice 2D Array into Smaller 2D Arrays
Pip Install from Pypi Works, But from Testpypi Fails (Cannot Find Requirements)
How to Change the X Axis in Matplotlib So There Is No White Space
Using an Numpy Array as Indices of the 2Nd Dim of Another Array
Creating an Empty Pandas Dataframe, Then Filling It
Stripping Everything But Alphanumeric Chars from a String in Python
How to Know If an Object Has an Attribute in Python
Using Pip to Install Packages to Anaconda Environment
How to Get the Caller's Method Name in the Called Method
How to Process Sigterm Signal Gracefully
Matplotlib (Pyplot) Savefig Outputs Blank Image
How to Embed Matplotlib in Pyqt - for Dummies
Downloading a Picture via Urllib and Python
Django Multivaluedictkeyerror Error, How to Deal with It
How to Enable Cors on Django Rest Framework
Importing Orange Returns "Importerror: No Module Named Orange"