I am trying to implement method overloading in Python:
class A:
def stackoverflow(self):
print 'first method'
def stackoverflow(self, i):
print 'second method', i
ob=A()
ob.stackoverflow(2)
but the output is second method 2; similarly:
class A:
def stackoverflow(self):
print 'first method'
def stackoverflow(self, i):
print 'second method', i
ob=A()
ob.stackoverflow()
gives
Traceback (most recent call last):
File "my.py", line 9, in <module>
ob.stackoverflow()
TypeError: stackoverflow() takes exactly 2 arguments (1 given)
How do I make this work?
It's method overloading, not method overriding. And in Python, you historically do it all in one function:
class A:
def stackoverflow(self, i='some_default_value'):
print('only method')
ob=A()
ob.stackoverflow(2)
ob.stackoverflow()
See the Default Argument Values section of the Python tutorial. See "Least Astonishment" and the Mutable Default Argument for a common mistake to avoid.
See PEP 443 for information about the single dispatch generic functions added in Python 3.4:
>>> from functools import singledispatch
>>> #singledispatch
... def fun(arg, verbose=False):
... if verbose:
... print("Let me just say,", end=" ")
... print(arg)
>>> #fun.register(int)
... def _(arg, verbose=False):
... if verbose:
... print("Strength in numbers, eh?", end=" ")
... print(arg)
...
>>> #fun.register(list)
... def _(arg, verbose=False):
... if verbose:
... print("Enumerate this:")
... for i, elem in enumerate(arg):
... print(i, elem)
You can also use pythonlangutil:
from pythonlangutil.overload import Overload, signature
class A:
#Overload
#signature()
def stackoverflow(self):
print('first method')
#stackoverflow.overload
#signature("int")
def stackoverflow(self, i):
print('second method', i)
While agf was right with the answer in the past, pre-3.4, now with PEP-3124 we got our syntactic sugar.
See typing documentation for details on the #overload decorator, but note that this is really just syntactic sugar and IMHO this is all people have been arguing about ever since.
Personally, I agree that having multiple functions with different signatures makes it more readable then having a single function with 20+ arguments all set to a default value (None most of the time) and then having to fiddle around using endless if, elif, else chains to find out what the caller actually wants our function to do with the provided set of arguments. This was long overdue following the Python Zen:
Beautiful is better than ugly.
and arguably also
Simple is better than complex.
Straight from the official Python documentation linked above:
from typing import overload
#overload
def process(response: None) -> None:
...
#overload
def process(response: int) -> Tuple[int, str]:
...
#overload
def process(response: bytes) -> str:
...
def process(response):
<actual implementation>
EDIT: for anyone wondering why this example is not working as you'd expect if from other languages I'd suggest to take a look at this discussion. The #overloaded functions are not supposed to have any actual implementation. This is not obvious from the example in the Python documentation.
In Python, you don't do things that way. When people do that in languages like Java, they generally want a default value (if they don't, they generally want a method with a different name). So, in Python, you can have default values.
class A(object): # Remember the ``object`` bit when working in Python 2.x
def stackoverflow(self, i=None):
if i is None:
print 'first form'
else:
print 'second form'
As you can see, you can use this to trigger separate behaviour rather than merely having a default value.
>>> ob = A()
>>> ob.stackoverflow()
first form
>>> ob.stackoverflow(2)
second form
You can't, never need to and don't really want to.
In Python, everything is an object. Classes are things, so they are objects. So are methods.
There is an object called A which is a class. It has an attribute called stackoverflow. It can only have one such attribute.
When you write def stackoverflow(...): ..., what happens is that you create an object which is the method, and assign it to the stackoverflow attribute of A. If you write two definitions, the second one replaces the first, the same way that assignment always behaves.
You furthermore do not want to write code that does the wilder of the sorts of things that overloading is sometimes used for. That's not how the language works.
Instead of trying to define a separate function for each type of thing you could be given (which makes little sense since you don't specify types for function parameters anyway), stop worrying about what things are and start thinking about what they can do.
You not only can't write a separate one to handle a tuple vs. a list, but also don't want or need to.
All you do is take advantage of the fact that they are both, for example, iterable (i.e. you can write for element in container:). (The fact that they aren't directly related by inheritance is irrelevant.)
I write my answer in Python 3.2.1.
def overload(*functions):
return lambda *args, **kwargs: functions[len(args)](*args, **kwargs)
How it works:
overload takes any amount of callables and stores them in tuple functions, then returns lambda.
The lambda takes any amount of arguments,
then returns result of calling function stored in functions[number_of_unnamed_args_passed] called with arguments passed to the lambda.
Usage:
class A:
stackoverflow=overload( \
None, \
#there is always a self argument, so this should never get called
lambda self: print('First method'), \
lambda self, i: print('Second method', i) \
)
I think the word you're looking for is "overloading". There isn't any method overloading in Python. You can however use default arguments, as follows.
def stackoverflow(self, i=None):
if i != None:
print 'second method', i
else:
print 'first method'
When you pass it an argument, it will follow the logic of the first condition and execute the first print statement. When you pass it no arguments, it will go into the else condition and execute the second print statement.
I write my answer in Python 2.7:
In Python, method overloading is not possible; if you really want access the same function with different features, I suggest you to go for method overriding.
class Base(): # Base class
'''def add(self,a,b):
s=a+b
print s'''
def add(self,a,b,c):
self.a=a
self.b=b
self.c=c
sum =a+b+c
print sum
class Derived(Base): # Derived class
def add(self,a,b): # overriding method
sum=a+b
print sum
add_fun_1=Base() #instance creation for Base class
add_fun_2=Derived()#instance creation for Derived class
add_fun_1.add(4,2,5) # function with 3 arguments
add_fun_2.add(4,2) # function with 2 arguments
In Python, overloading is not an applied concept. However, if you are trying to create a case where, for instance, you want one initializer to be performed if passed an argument of type foo and another initializer for an argument of type bar then, since everything in Python is handled as object, you can check the name of the passed object's class type and write conditional handling based on that.
class A:
def __init__(self, arg)
# Get the Argument's class type as a String
argClass = arg.__class__.__name__
if argClass == 'foo':
print 'Arg is of type "foo"'
...
elif argClass == 'bar':
print 'Arg is of type "bar"'
...
else
print 'Arg is of a different type'
...
This concept can be applied to multiple different scenarios through different methods as needed.
In Python, you'd do this with a default argument.
class A:
def stackoverflow(self, i=None):
if i == None:
print 'first method'
else:
print 'second method',i
Python does not support method overloading like Java or C++. We may overload the methods, but we can only use the latest defined method.
# First sum method.
# Takes two argument and print their sum
def sum(a, b):
s = a + b
print(s)
# Second sum method
# Takes three argument and print their sum
def sum(a, b, c):
s = a + b + c
print(s)
# Uncommenting the below line shows an error
# sum(4, 5)
# This line will call the second sum method
sum(4, 5, 5)
We need to provide optional arguments or *args in order to provide a different number of arguments on calling.
Courtesy Python | Method Overloading
I just came across overloading.py (function overloading for Python 3) for anybody who may be interested.
From the linked repository's README file:
overloading is a module that provides function dispatching based on
the types and number of runtime arguments.
When an overloaded function is invoked, the dispatcher compares the
supplied arguments to available function signatures and calls the
implementation that provides the most accurate match.
Features
Function validation upon registration and detailed resolution rules
guarantee a unique, well-defined outcome at runtime. Implements
function resolution caching for great performance. Supports optional
parameters (default values) in function signatures. Evaluates both
positional and keyword arguments when resolving the best match.
Supports fallback functions and execution of shared code. Supports
argument polymorphism. Supports classes and inheritance, including
classmethods and staticmethods.
Python 3.x includes standard typing library which allows for method overloading with the use of #overload decorator. Unfortunately, this is to make the code more readable, as the #overload decorated methods will need to be followed by a non-decorated method that handles different arguments.
More can be found here here but for your example:
from typing import overload
from typing import Any, Optional
class A(object):
#overload
def stackoverflow(self) -> None:
print('first method')
#overload
def stackoverflow(self, i: Any) -> None:
print('second method', i)
def stackoverflow(self, i: Optional[Any] = None) -> None:
if not i:
print('first method')
else:
print('second method', i)
ob=A()
ob.stackoverflow(2)
Python added the #overload decorator with PEP-3124 to provide syntactic sugar for overloading via type inspection - instead of just working with overwriting.
Code example on overloading via #overload from PEP-3124
from overloading import overload
from collections import Iterable
def flatten(ob):
"""Flatten an object to its component iterables"""
yield ob
#overload
def flatten(ob: Iterable):
for o in ob:
for ob in flatten(o):
yield ob
#overload
def flatten(ob: basestring):
yield ob
is transformed by the #overload-decorator to:
def flatten(ob):
if isinstance(ob, basestring) or not isinstance(ob, Iterable):
yield ob
else:
for o in ob:
for ob in flatten(o):
yield ob
In the MathMethod.py file:
from multipledispatch import dispatch
#dispatch(int, int)
def Add(a, b):
return a + b
#dispatch(int, int, int)
def Add(a, b, c):
return a + b + c
#dispatch(int, int, int, int)
def Add(a, b, c, d):
return a + b + c + d
In the Main.py file
import MathMethod as MM
print(MM.Add(200, 1000, 1000, 200))
We can overload the method by using multipledispatch.
There are some libraries that make this easy:
functools - if you only need the first argument use #singledispatch
plum-dispatch - feature rich method/function overloading.
multipledispatch - alternative to plum less features but lightweight.
python 3.5 added the typing module. This included an overload decorator.
This decorator's intended purpose it to help type checkers. Functionally its just duck typing.
from typing import Optional, overload
#overload
def foo(index: int) -> str:
...
#overload
def foo(name: str) -> str:
...
#overload
def foo(name: str, index: int) -> str:
...
def foo(name: Optional[str] = None, index: Optional[int] = None) -> str:
return f"name: {name}, index: {index}"
foo(1)
foo("bar", 1)
foo("bar", None)
This leads to the following type information in vs code:
And while this can help, note that this adds lots of "weird" new syntax. Its purpose - purely type hints - is not immediately obvious.
Going with Union of types usually is a better option.
Related
I want to write a wrapper function for a known function, like
def wrapper(*args, **kwargs)
foo()
return known_function(*args, **kwargs)
How can i add type-annotations to wrapper, such that it exactly follows the type annotations of known_function
I have looked at ParamSpec, but it appears to only work when the wrapper-function is generic and takes the inner function as argument.
P = ParamSpec("P")
T = TypeVar('T')
def wrapper(func_arg_that_i_dont_want: Callable[P,T], *args: P.args, **kwargs: P.kwargs)
foo()
return known_function(*args, **kwargs)
Can i force the P to only be valid for known_function, without linking it to a Callable-argument?
PEP 612 as well as the documentation of ParamSpec.args and ParamSpec.kwargs are pretty clear on this:
These “properties” can only be used as the annotated types for *args and **kwargs, accessed from a ParamSpec already in scope.
- Source: PEP 612 ("The components of a ParamSpec" -> "Valid use locations")
Both attributes require the annotated parameter to be in scope.
- Source: python.typing module documentation (class typing.ParamSpec -> args/kwargs)
They [parameter specifications] are only valid when used in Concatenate, or as the first argument to Callable, or as parameters for user-defined Generics.
- Source: python.typing module documentation (class typing.ParamSpec, second paragraph)
So no, you cannot use parameter specification args/kwargs, without binding it a concrete Callable in the scope you want to use them in.
I question why you would even want that. If you know that wrapper will always call known_function and you want it to (as you said) have the exact same arguments, then you just annotate it with the same arguments. Example:
def known_function(x: int, y: str) -> bool:
return str(x) == y
def wrapper(x: int, y: str) -> bool:
# other things...
return known_function(x, y)
If you do want wrapper to accept additional arguments aside from those passed on to known_function, then you just include those as well:
def known_function(x: int, y: str) -> bool:
return str(x) == y
def wrapper(a: float, x: int, y: str) -> bool:
print(a ** 2)
return known_function(x, y)
If your argument is that you don't want to repeat yourself because known_function has 42 distinct and complexly typed parameters, then (with all due respect) the design of known_function should be covered in copious amounts gasoline and set ablaze.
If you insist to dynamically associate the parameter specifications (or are curious about possible workarounds for academic reasons), the following is the best thing I can think of.
You write a protected decorator that is only intended to be used on known_function. (You could even raise an exception, if it is called with anything else to protect your own sanity.) You define your wrapper inside that decorator (and add any additional arguments, if you want any). Thus, you'll be able to annotate its *args/**kwargs with the ParamSpecArgs/ParamSpecKwargs of the decorated function. In this case you probably don't want to use functools.wraps because the function you receive out of that decorator is probably intended not to replace known_function, but stand alongside it.
Here is a full working example:
from collections.abc import Callable
from typing import Concatenate, ParamSpec, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
def known_function(x: int, y: str) -> bool:
"""Does thing XY"""
return str(x) == y
def _decorate(f: Callable[P, T]) -> Callable[Concatenate[float, P], T]:
if f is not known_function: # type: ignore[comparison-overlap]
raise RuntimeError("This is an exclusive decorator.")
def _wrapper(a: float, /, *args: P.args, **kwargs: P.kwargs) -> T:
"""Also does thing XY, but first does something else."""
print(a ** 2)
return f(*args, **kwargs)
return _wrapper
wrapper = _decorate(known_function)
if __name__ == "__main__":
print(known_function(1, "2"))
print(wrapper(3.14, 10, "10"))
Output as expected:
False
9.8596
True
Adding reveal_type(wrapper) to the script and running mypy gives the following:
Revealed type is "def (builtins.float, x: builtins.int, y: builtins.str) -> builtins.bool"
PyCharm also gives the relevant suggestions regarding the function signature, which it infers from having known_function passed into _decorate.
But again, just to be clear, I don't think this is good design. If your "wrapper" is not generic, but instead always calls the same function, you should explicitly annotate it, so that its parameters correspond to that function. After all:
Explicit is better than implicit.
- Zen of Python, line 2
Suppose I've got a map like function:
def generate(data, per_element):
for element in data:
per_element(element)
How can I add type-hints so that if I call generate(some_data, some_function) where some_data: List[SomeClass], I get a warning if SomeClass is missing a field used by some_function?
As an example - with the following code:
def some_function(x):
print(x.value)
some_data: List[int] = [1, 2, 3]
generate(some_data, some_function)
I would like to get a warning that int does not have the attribute value.
Use a type variable to make generate generic in the type of object that data contains and that per_element expects as an argument.
from typing import TypeVar, List, Callable
T = TypeVar('T')
def generate(data: List[T], per_element: Callable[[T], Any]):
for element in data:
per_element(element)
class Foo:
def __init__(self):
self.value = 3
def foo(x: Foo):
print(x.value)
def bar(x: int):
pass
generate([Foo(), Foo()], foo) # OK
# Argument 2 to "generate" has incompatible type "Callable[[Foo], Any]"; expected "Callable[[int], Any]"
generate([1,2,3], foo)
Whatever T is, it has to be the same type for both the list and the function, to ensure that per_element can, in fact, be called on every value in data. The error produced by the second call to generate isn't exactly what you asked for, but it essentially catches the same problem: the list establishes what type T is bound to, and the function doesn't accept the correct type.
If you specifically want to require that T be a type whose instances have a value attribute, it's a bit trickier. It's similar to the use case for Protocol, but that only supports methods (or class attributes in general?), not instance attributes, as far as I know. Perhaps someone else can provide a better answer.
Seems like you're searching for:
def generate(data: List[AClass], per_element):
for element in data:
per_element(element)
So that AClass implements the method you need.
Your class needs the value attribute:
class SomeClass:
value: Any # I used any but use whatever type hint is appropriate
Then using typing.Callable in your function as well as the builtin types. starting with python 3.7 and finally fully implemented in python 3.9 you can use the builtins themselves as well as in python 3.9 you can use parameter specifications
from typing import ParamSpec, TypeVar, Callable
P = ParamSpec("P")
R = TypeVar("R")
def generate(data: list[SomeClass], per_element: Callable[P, R]) -> None:
for element in data:
per_element(element)
Then in some_function using the class type hint and None return variable:
def some_function(x: SomeClass) -> None:
print(x.value)
There is a function (f) which consumes a function signature (g) that takes a known first set of arguments and any number of keyword arguments **kwargs. Is there a way to include the **kwargs in the type signature of (g) that is described in (f)?
For example:
from typing import Callable, Any
from functools import wraps
import math
def comparator(f: Callable[[Any, Any], bool]) -> Callable[[str], bool]:
#wraps(f)
def wrapper(input_string: str, **kwargs) -> bool:
a, b, *_ = input_string.split(" ")
return f(eval(a), eval(b), **kwargs)
return wrapper
#comparator
def equal(a, b):
return a == b
#comparator
def equal_within(a, b, rel_tol=1e-09, abs_tol=0.0):
return math.isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol)
# All following statements should print `True`
print(equal("1 1") == True)
print(equal("1 2") == False)
print(equal_within("5.0 4.99998", rel_tol=1e-5) == True)
print(equal_within("5.0 4.99998") == False)
The function comparator wraps its argument f with wrapper, which consumes the input for f as a string, parses it and evaluates it using f. In this case, Pycharm gives a warning that return f(eval(a), eval(b), **kwargs) calls f with the unexpected argument **kwargs, which doesn't match the expected signature.
This post on Reddit suggests adding either Any or ... to the type signature of f like
f: Callable[[Any, Any, ...], bool]
f: Callable[[Any, Any, Any], bool]
The former causes a TypeError [1], while the latter seems to misleading, since f accepts at least 2 arguments, rather than exactly 3.
Another workaround is to leave the Callable args definition open with ... like f: Callable[..., bool], but I'm wondering if there is a more appropriate solution.
TypeError: Callable[[arg, ...], result]: each arg must be a type. Got Ellipsis.
tl;dr: Protocol may be the closest feature that's implemented, but it's still not sufficient for what you need. See this issue for details.
Full answer:
I think the closest feature to what you're asking for is Protocol, which was introduced in Python 3.8 (and backported to older Pythons via typing_extensions). It allows you to define a Protocol subclass that describes the behaviors of the type, pretty much like an "interface" or "trait" in other languages. For functions, a similar syntax is supported:
from typing import Protocol
# from typing_extensions import Protocol # if you're using Python 3.6
class MyFunction(Protocol):
def __call__(self, a: Any, b: Any, **kwargs) -> bool: ...
def decorator(func: MyFunction):
...
#decorator # this type-checks
def my_function(a, b, **kwargs) -> bool:
return a == b
In this case, any function that have a matching signature can match the MyFunction type.
However, this is not sufficient for your requirements. In order for the function signatures to match, the function must be able to accept an arbitrary number of keyword arguments (i.e., have a **kwargs argument). To this point, there's still no way of specifying that the function may (optionally) take any keyword arguments. This GitHub issue discusses some possible (albeit verbose or complicated) solutions under the current restrictions.
For now, I would suggest just using Callable[..., bool] as the type annotation for f. It is possible, though, to use Protocol to refine the return type of the wrapper:
class ReturnFunc(Protocol):
def __call__(self, s: str, **kwargs) -> bool: ...
def comparator(f: Callable[..., bool]) -> ReturnFunc:
....
This gets rid of the "unexpected keyword argument" error at equal_within("5.0 4.99998", rel_tol=1e-5).
With PEP 612 in Python 3.10, you may try the following solution:
from typing import Callable, Any, ParamSpec, Concatenate
from functools import wraps
P = ParamSpec("P")
def comparator(f: Callable[Concatenate[Any, Any, P], bool]) -> Callable[Concatenate[str, P], bool]:
#wraps(f)
def wrapper(input_string: str, *args: P.args, **kwargs: P.kwargs) -> bool:
a, b, *_ = input_string.split(" ")
return f(eval(a), eval(b), *args, **kwargs)
return wrapper
However it seems that you cannot get rid of *args: P.args (which you actually don't need) as PEP 612 requires P.args and P.kwargs to be used together. If you can make sure that your decorated functions (e.g. equal and equal_within) do not take extra positional arguments, any attempts to call the functions with extra positional arguments should be rejected by the type checker.
I'm trying to implement strong type genetic programming in python.
Is there something like these sample?
def funcA(a,b):
return a + b
return_type(funcA)
output: <class 'Integer'>
and
def funcA(a,b):
return a + b
parameter_type(funcA)
output: [<class 'Integer'>,<class 'Integer'>]
update:
I'm trying to generate python's expression and avoiding something cannot be evaluated like this:
funcA(20, funcA(True, "text"))
In Python, a dynamically typed language, the type information of a function's parameters is required at runtime. In 3.3 and later, you can get the type of a function as follows:
from inspect import signature
def foo(a, *, b:int, **kwargs):
... pass
sig = signature(foo)
str(sig)
'(a, *, b:int, **kwargs)'
str(sig.parameters['b'])
'b:int'
sig.parameters['b'].annotation
<class 'int'>
see https://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object
Python 3 introduces function annotations. By themselves they don't do anything, but you can write your own enforcement:
def strict(fun):
# inspect annotations and check types on call
#strict
def funcA(a: int, b: int) -> int:
return a + b
you can check that with annotations:
>>> def func(a: str) -> int:
# code
>>> func.__annotations__["return"]
<class 'int'>
and the same with parameters:
>>> func.__annotations__["a"]
<class 'str'>
In Python return type is not known until the call is performed and return statement is executed. It even can be different in different situations, so a brief answer is "not possible".
If you need to know the return type for certain function, you still may wrap it into some type checking code that also may expose the return type. However, that would be rather unpythonic:
def declare_return_type(t):
def decorator(f):
def wrapper(*a, **kw):
res = f(*a, **kw)
assert isinstance(res, t)
return res
wrapper.return_type = t
return wrapper
return decorator
#declare_return_type(int)
def f(a, b):
return a + b
print f.return_type
print f(1, 2) # ok
f('a', 'b') # Assertion error
UPD: You may do the same to parameter types and check them as well.
the best way is to use docstrings to store such information of the function and
In [49]: def funcA(a,b):
....: ''' returns an int '''
....: return int(a+b)
....:
In [50]: funcA.__doc__
Out[50]: ' returns an int '
no chance. since python uses duck typing, you could pass parameters of different types, e.g. int and int, str and str, etc. to funcA. there is no chance to tell what return type and parameter type can be without seeing actual parameters
It is impossible to know from just the function as given that it should only be valid for integers. If you call it with integer parameters:
funcA(1, 2)
You get 3, an Integer, but what about this:
funcA("Test", "Test")
You get "TestTest", a string! This technically works, but whether or not it should be used that way is information that just doesn't exist in the code you provide.
In more modern Python, annotations provide the potentially to explicitly state this kind of information, or potentially you could try to infer it from existing usage or from the code of the function, but which of those options (if any) make sense for your use case will depend heavily on the specifics of what you want to do.
Is it possible to have overloaded functions in Python?
In C# I would do something like
void myfunction (int first, string second)
{
# Some code
}
void myfunction (int first, string second, float third)
{
# Some different code
}
And then when I call the function it would differentiate between the two based on the number of arguments. Is it possible to do something similar 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
In normal Python you can't do what you want. There are two close approximations:
def myfunction(first, second, *args):
# 'args' is a tuple of extra arguments
def myfunction(first, second, third=None):
# 'third' is optional
However, if you really want to do this, you can certainly make it work (at the risk of offending the traditionalists ;o). In short, you would write a wrapper(*args) function that checks the number of arguments and delegates as appropriate. This kind of "hack" is usually done via decorators. In this case, you could achieve something like:
from typing import overload
#overload
def myfunction(first):
....
#myfunction.overload
def myfunction(first, second):
....
#myfunction.overload
def myfunction(first, second, third):
....
And you'd implement this by making the overload(first_fn) function (or constructor) return a callable object where the __call__(*args) method does the delegation explained above and the overload(another_fn) method adds extra functions that can be delegated to.
You can see an example of something similar here http://acooke.org/pytyp/pytyp.spec.dispatch.html, but that is overloading methods by type. It's a very similar approach...
And something similar (using argument types) is being added to Python 3 - PEP 443 -- Single-dispatch generic functions
Yes, it's possible. I wrote the code below in Python 3.2.1:
def overload(*functions):
return lambda *args, **kwargs: functions[len(args)](*args, **kwargs)
Usage:
myfunction=overload(no_arg_func, one_arg_func, two_arg_func)
Note that the lambda returned by the overload functions choose a function to call depending on the number of unnamed arguments.
The solution isn't perfect, but at the moment I can't write anything better.
It is not possible directly. You can use explicit type checks on the arguments given though, although this is generally frowned upon.
Python is dynamic. If you are unsure what an object can do, just try: and call a method on it, then except: errors.
If you don't need to overload based on types, but just on the number of arguments, use keyword arguments.
Overloading methods is tricky in Python. However, there could be usage of passing the dict, list or primitive variables.
I have tried something for my use cases, and this could help here to understand people to overload the methods.
Let's take the example use in one of the Stack Overflow questions:
A class overload method with call the methods from different class.
def add_bullet(sprite=None, start=None, headto=None, spead=None, acceleration=None):
Pass the arguments from a remote class:
add_bullet(sprite = 'test', start=Yes, headto={'lat':10.6666, 'long':10.6666}, accelaration=10.6}
Or
add_bullet(sprite = 'test', start=Yes, headto={'lat':10.6666, 'long':10.6666}, speed=['10','20,'30']}
So, handling is being achieved for list, Dictionary or primitive variables from method overloading.
Try it out for your code.
Here is the way to overload python functions with default arguments as well as keyword arguments
from multipledispatch import dispatch
# FOR hi(a: int, b: int = 3)
#dispatch(int, int)
def _hi(a: int, b: int):
print(a, b)
#dispatch(int, int)
def hi(a: int, b: int = 3):
_hi(a, b)
#dispatch(int, b=int)
def hi(a: int, *, b: int = 3):
_hi(a, b)
# FOR hi(a: str, b: int = 3)
#dispatch(str, int)
def _hi(a: str, b: int):
print(a, b, 'str!')
#dispatch(str, int)
def hi(a: str, b: int = 3):
_hi(a, b)
#dispatch(str, b=int)
def hi(a: str, *, b: int = 3):
_hi(a, b)
hi(2)
hi(2, 3)
hi(2, b=3)
hi('2')
hi('2', 3)
hi('2', b=3)
Output
2 3
2 3
2 3
2 3 str!
2 3 str!
2 3 str!