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.
You need to add the arg to wrapper and then some_function:
def timing_function(some_function):
def wrapper(arg):
t1 = time.time()
some_function(arg)
t2 = time.time()
return "Time it took to run: " + str((t2-t1)) + "\n"
return wrapper
If you want to use the decorator on different functions use *args and ** kwargs which will also work for functions that take no args:
def timing_function(some_function):
def wrapper(*args,**kwargs):
t1 = time.time()
some_function(*args,**kwargs)
t2 = time.time()
return "Time it took to run: " + str((t2-t1)) + "\n"
return wrapper
On a side note you may find the timeit module useful if you want to time your code, if you have ipython installed you can simply use timeit your_function
.
Why do all decorator examples I've seen always have an internal function, can you not write a decorator as one function?
No, A decorator in Python is a callable Python object that is used to modify a function, method or class definition. The original object, the one which is going to be modified, is passed to a decorator as an argument. The decorator returns a modified object, e.g. a modified function, which is bound to the name used in the definition, there are more examples here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With