Python decorator function - ghdrako/doc_snipets GitHub Wiki

Decoration converts the original function to a closure, which is an inner function created from the decorator.

import random 
import time 
def logging_time(func):
  def logger(*args, **kwargs):
    print(f"--- {func.__name__} starts")  
    start_t = time.time()  
    value_returned = func(*args, **kwargs)  
    end_t = time.time()  
    print(f"*** {func.__name__} ends; used time: {end_t - start_t:.2f} s")  
    return value_returned  
  return logger 

@logging_time 
def example_func2():
  random_delay = random.randint(3, 5) * 0.1  
  time.sleep(random_delay) 

example_func2() # output the following two lines:
--- example_func2 starts *** example_func2 ends; used time: 0.40 s

To maximize decorators’ flexibility, it’s essential to include *args and **kwargs in the inner function, because the created inner function will be the decorated function, and using *args and **kwargs makes the inner function compat-ible with any calling signature.

When we define the inner function, we shouldn’t forget to add the return statement. Specifically, the return value should be the one that you get by calling the decorated function.

When we want to apply operations after calling the decorated function, we use a temporary variable to store the return value. After the extra operations, we return this variable. This is exactly what we did for the logging_time function

def timing_decorator(func):
  def wrapper(delay):
    start_time = time()
    print("starting timing")
    result = func(delay)
    print(f"task elapsed time: {time() - start_time}")
    return result
  return wrapper