I'm learning to use decorators at the moment but am struggling to wrap my head around their purpose and utility.
I initially thought they provided the convenient option to add extra functionality to an existing function (e.g. func()
) without changing its source code, but if the additional functionality is executed whenever func()
is called thereafter, then why wouldn't you just save the time/space/complexity and add the new functionality to func()
directly?
E.g. Say I wanted to make a function print whenever it is executed, then wouldn't this:
def add(*args):
out = sum(args)
print("Function 'add' executed.")
return out
create the exact same function as below, with far less code/complexity?
def log(func):
def wrapper(*args, **kwargs):
out = func(*args, **kwargs)
print(f"Function '{func.__name__}' executed.")
return out
return wrapper
@log
def add(*args):
return sum(args)
Off the top of my head, the only cases I can think of where the latter could potentially be preferable is if you're importing a generalised decorator to modify a function defined in a separate script, or are applying the same func to many functions so are saving space by just putting it in its own function (in which case it would seem more reasonable to just write a regular old function to simply execute it normally inside others).
EDIT
Perhaps a better way to formulate this question: Which of the following is preferable, and why?
def logger():
# New functionality here
return
def func1(*args):
# func1 functionality
logger()
return
def func2(*args):
# func2 functionality
logger()
return
Or
def logger(func):
def wrapper(*args, **kwargs):
out = func(*args, **kwargs)
# New functionality here
return out
return wrapper
@logger
def func1(*args):
# func1 functionality
return
@logger
def func2(*args):
# func2 functionality
return