Reputation: 23
I'm extremely new to python, and i just encountered decorators. I'm still kinda confused by them but i am learning
i was trying to make a decorator that tells me how much time my function took to finish, but apparently when i try to use it on a function that should return something, it just returns "None"
I've seen only a couple of questions talking about this problem but none of them actually helped
Here's my code
import time
def time_it(func): # Here i make a simple decorator function that should time my decorated function
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args)
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
return wrapper
@time_it
def square(nums): # I make a function that squares every number in a list
new_list = []
for n in nums:
new_list.append(n ** 2)
return new_list
lis = [f for f in range(200000)] # i make a list with a range of 200000
print(square(lis))
sorry for any grammatical errors, i'm not a native english speaker
Upvotes: 2
Views: 1773
Reputation: 429
The problem is that your inner function return value isn't being returned. The change is noted below:
from functools import wraps
def time_it(func): # Here i make a simple decorator function that should time my decorated function
@wraps(func)
def wrapper(*args, **kwargs):
t1 = time.time()
## Note the change on this line -- I now store the return result from the called function
result = func(*args, **kwargs)
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
## And then explicitly return the result
return result
return wrapper
For the decorator, you need to remember that it's just a closure, with some fancy syntax. You still need to deal with the function return parameters yourself.
A couple of additions:
from functools import wraps
and @wraps(func)
Upvotes: 5
Reputation: 19352
The decorator replaces square
with wrapper
and wrapper
does not return anything. It should return the value returned by the wrapped function.
This is the correct way to do it:
def time_it(func):
def wrapper(*args, **kwargs):
t1 = time.time()
try:
return func(*args, **kwargs)
finally:
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
return wrapper
I changed 3 things:
return
, so that the value is returned from decorated function**kwargs
to func
calls, because it may be needed if used differentlytry
/finally
block, so that the printout happens even in case of an exception, plus this makes it easier to return the value.Upvotes: 4
Reputation: 24233
Your decorated function doesn't return anything explicitely - so, by default, it returns None
.
You can capture the output before printing the time, and return at the end:
def time_it(func): # Here i make a simple decorator function that should time my decorated function
def wrapper(*args, **kwargs):
t1 = time.time()
out = func(*args)
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
return out
return wrapper
Upvotes: 0