I am trying to write python decorators and I am having problems understanding how the internal wrapper takes arguments. I have here:
import time
def timing_function(some_function):
def wrapper():
t1 = time.time()
some_function()
t2 = time.time()
return "Time it took to run: " + str((t2-t1)) + "\n"
return wrapper
@timing_function
def my_function(x):
return x * x
my_function(6)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-fe2786a2753c> in <module>()
----> 1 my_function(6)
TypeError: wrapper() takes no arguments (1 given)
Which is slightly different than the example:
import time
def timing_function(some_function):
"""
Outputs the time a function takes
to execute.
"""
def wrapper():
t1 = time.time()
some_function()
t2 = time.time()
return "Time it took to run the function: " + str((t2-t1)) + "\n"
return wrapper
@timing_function
def my_function():
num_list = []
for x in (range(0,10000)):
num_list.append(x)
return "\nSum of all the numbers: " +str((sum(num_list)))
print my_function()
Time it took to run the function: 0.0
It seems the problem is the 'x' argument. I tried giving wrapper *args, but it also did not work. My questions are
What is the proper way to allow arguments in this simple wrapper? Thank you
Why do all decorator examples I've seen always have an internal function, can you not write a decorator as one function?
Thank you
You need to pass the argument from my_function
to wrapper
, i.e.:
def wrapper(x):
If you want it to be able to handle many more functions generically then you'd have to do something like:
def wrapper(*args, **kwargs):
But then your logic within the decorator would also need to be able to handle the args
and kwargs
generically.
Because a decorator is a function that takes a function as an argument and returns a function that is executed as a wrapper of the original function. In fact, decorators are often written as three functions:
from functools import wraps
def somedec(somearg, someopt=None):
def somedec_outer(fn):
@wraps(fn)
def somedec_inner(*args, **kwargs):
# do stuff with somearg, someopt, args and kwargs
response = fn(*args, **kwargs)
return response
return somedec_inner
return somedec_outer
Why do this? You can pass some information to the decorator based on the type of function you're decorating, or maybe different ways the decorator behaves.
@somedec(30.0, 'foobarbaz')
def somefn(a, b, c):
return a + b + c
@somedec(15.0, 'quxjinzop')
def otherfn(d, e, f):
return d - e - f
functools.wraps
will make the decorated function look like the original function to the Python interpreter. This is helpful for logging and debugging and such and is a best practice to use when creating decorators.