I have a lot of functions like the following, which recursively call themselves to get one or many returns depending on the type of the argument:
def get_data_sensor(self, sensorname):if isinstance(sensorname, list):return [get_data_sensor(self, sensor) for sensor in sensorname]return get_data(os.path.join(self.get_info("path"), "{0}.npy".format(sensorname)))
I would like to call my function recursively without having to know my current function name, I don't want to have my function name twice in the code to limit copy-paste error.
Determine function name from within that function (without using traceback) shows how to get the actual function name, but I need the function itself to call it.
You can abstract that logic outside the function entirely by using a decorator (see this thorough answer if you're unfamiliar with decorators):
from functools import wrapsdef autolist(func):@wraps(func)def wrapper(args):if isinstance(args, list):return [func(arg) for arg in args]return func(args)return wrapper
This decorator can be applied to any function requiring the pattern, which now only needs to implement the scalar case:
>>> @autolist
... def square(x):
... return x ** 2
...
>>> square(1)
1
>>> square([1, 2, 3])
[1, 4, 9]
If you're applying it to a method, as self
implies, you'll also need to take that argument into account in the wrapper
. For example, if the relevant argument is always the last one you could do:
def autolist(func):@wraps(func)def wrapper(*args):*args, last_arg = argsif isinstance(last_arg, list):return [func(*args, arg) for arg in last_arg]return func(*args, last_arg)return wrapper
This would work on methods, too:
>>> class Squarer:
... @autolist
... def square(self, x):
... return x ** 2
...
>>> Squarer().square(1)
1
>>> Squarer().square([1, 2, 3])
[1, 4, 9]