Decorators¶
Functions are things that generate values based on arguments. In Python, functions are first-class objects. This means that you can bind names to them, pass them around, etc., just like other objects. Thanks to this you can write functions that take functions as arguments or return functions as values.
def substitute(a_function):
def new_function(*args, **kwargs):
return "I'm not that other function"
return new_function
“A decorator is a function that takes a function as an argument and returns a function as a return value.”
That’s nice, but why is it useful? Imagine you are trying to debug a module with a number of functions like this one:
def add(a, b):
return a + b
def add(a, b):
print("Function 'add' called with args: {}, {}".format(a, b) )
result = a + b
print("\tResult --> {}".format(result))
return result
def logged_func(func):
def logged(*args, **kwargs):
print("Function {} called".format(func.__name__))
if args:
print("\twith args: {}".format(args))
if kwargs:
print("\twith kwargs: {}".format(kwargs))
result = func(*args, **kwargs)
print("\t Result --> {}".format(result))
return result
return logged
We could then make logging versions of our module functions.
logging_add = logged_func(add)
Then, where we want to see the results, we can use the logged version:
In []: logging_add(3, 4)
Function 'add' called
with args: (3, 4)
Result --> 7
Out[]: 7
def logged_func(func):
# implemented above
def add(a, b):
return a + b
add = logged_func(add)
And now you can simply use the code you’ve already written and calls to ``add`` will be logged:
In []: add(3, 4)
Function 'add' called
with args: (3, 4)
Result --> 7
Out[]: 7