For example, if we want to time the execution of a function the not optimal way.
import timedef cubeit(x):
time.sleep(1)
return x * x * xt_start = time.perf_counter()
res = cubeit(144)
t_end = time.perf_counter()
t_run = t_end - t_start
print(f'It took cubeit {t_run:.2f} sec to complete')
print(res)
It took cubeit 1.00 sec to complete
2985984
It’s not optimal! What if you want to perform the same task on another function.
The High order function approach, passing the function cubeit to our timer function.
import timedef timer_func(f):
def wrapper_timer(*args):
t_start = time.perf_counter()
res = f(*args)
t_end = time.perf_counter()
t_run = t_end - t_start
print(f'It took {f.__name__} {t_run:.2f} sec to complete')
return res
return wrapper_timerdef cubeit(x):
time.sleep(1)
return x * x * xfoo = timer_func(cubeit)
res = foo(144)
print(res)
It took cubeit 1.00 sec to complete
2985984
Well, this is cluttered! is there a neat way to achieve the same result?
Yes, the pythonic way of using decorators.
import timedef timer_func(f):
def wrapper_timer(*args):
t_start = time.perf_counter()
res = f(*args)
t_end = time.perf_counter()
t_run = t_end - t_start
print(f'It took {f.__name__} {t_run:.2f} sec to complete')
return res
return wrapper_timer@timer_func
def cubeit(x):
time.sleep(1)
return x * x * xres = cubeit(144)
print(res)It took cubeit 1.00 sec to complete
2985984
Note that @timer_fun is equivalent to timer_fun(cubeit).
So, what else can I do with decorators, logging to an external file!