Consider the following functions:
def function_a(self, index):
# ..do something with index and return the result
return index * index
def function_b(self, group, arg):
# ..do something with group and arg to derive an index and call function_a
index = group + arg
return function_a(index)
def function_c(self, group, arg_mult):
# ..do something with group and arg_mult to derive an index and call function_a
index = group * arg_mult
return function_a(index)
Assume there are multiple functions in this module that take an index as parameter. Each of those functions should be callable with other parameter combinations which are then converted into an index with which the original function is called. The allowed combinations of parameters are the same for each function (in the example above: group and arg or group and arg_mult). I wrote a decorator for that called convert_parameters and decorated function_a with it. function_b and function_c can then be omitted.
import functools
def convert_parameters(func):
@functools.wraps(func)
def wrapper(self, *args, arg_index=None, group=None, arg=None, arg_mult=None, **kwargs):
index = None
if arg_index is not None:
index = arg_index
elif group is not None:
if arg is not None:
index = group + arg
elif arg_mult is not None:
index = group * arg_mult
if index is None:
raise ValueError("invalid parameter combination provided.")
return func(self, *args, index=index, **kwargs)
return wrapper
@convert_parameters
def function_a(self, index):
# ..do something with index and return the result
return index * index
However, I am not sure if this is a very elegant solution, since the caller must know which argument combinations are valid for function_a (or any other function decorated with convert_parameters). This would basically require a look into the code. I would rather have it that the valid signatures for those functions are known to the caller, much like emulating the behavior of @overload but with my custom decorator.