Decorators (identified by @ in line above function definition) allow code to be run before and after the function (hence ‘decorating’ it). An example of use of a decorator is shown below when a decorator function is used to time two different functions. This removes the need for duplicating code in different functions, and also keeps the functions focussed on their primary objective.
A decorator function is written so that is becomes a wrapper around the function it decorates. The wrapper passes arguments (args) and named arguments (kwargs) to the function it wraps around, so that the wrapped function behaves as if it were running normally.
In the example below we define a decorator function that records the time before and after the decorated function, and displays the time taken.
Example of simple function decorator ------------------------------------ @time_it is a function decorator. It takes the function below it and passes it, along with the passed arguments, to the time_it function. This allows multiple functions to be timed without replication of code, and keeps each function code clean and uncluttered. """ # The decorator will pass the decorated function to this function def time_it(func): import time # The following wrapper is common to all decorators def wrapper (*args, **kwargs): # Code that runs before the 'wrapped' function start = time.time() # Run the 'wrapped' function result = func(*args, **kwargs) # Code that runs after the 'wrapped' function end = time.time() print (func.__name__ + ' took ' + str(int((end-start)*1000)) + ' milliseconds') return result return wrapper @time_it def calc_square(numbers): result =  for number in numbers: result.append (number ** 2) return result @time_it def calc_cube(numbers): result =  for number in numbers: result.append (number ** 3) return result if __name__ == '__main__': x = range(1,10000) calc_square(x) calc_cube(x)
calc_square took 4 milliseconds
calc_cube took 4 milliseconds