# Python - Decorators

Decorators are powerful and useful tool in Python. It adds functionality to an existing code and modify the behaviour of the code while compiling it. for example - It allows a programmer to wrap a function (a.k.a wrapper function) around a given function (a.k.a wrapped function) in order to modify the behavior of the given function, without permanently modifying it.

## Create a Decorator

The decorator is created exactly same as any function in python and takes function as an argument. Inside decorator, a wrapper function is created which is wrapped around the function which is passed as an argument. Please see the syntax below:

### Syntax

# defining decorator
def decorator_name(funct):
def wrapper_function():
statements
funct
statements
return wrapped_function


## Use a Decorator

To use a decorator with a function, a line containing @decorator name must be added above the function definition as shown in syntax below. Along with this, The argument definition of wrapped function must match inside and outside of the decorator.

### Syntax

# use decorator with wrapped function
@decorator_name
def wrapped_function():
statements


### Example: function with no argument and not a return type

In the below example, a decorator called MyDecorator is created. Inside it, a wrapper function called MyWrapperFunction is created which prints a message just before and after execution of the wrapped function called MyFunction. The argument definition of wrapped function must match inside and outside of the decorator.

def MyDecorator(funct):
def MyWrapperFunction():
print("Before Execution.")
funct()
print("After Execution.")
return MyWrapperFunction

@MyDecorator
def MyFunction():
print("Hello World!.")

MyFunction()


Output

Before Execution.
Hello World!.
After Execution.


### Example: function with arguments and not a return type

In the below example, wrapped function called MyFunction takes two numbers as arguments and prints the sum of those numbers with a given message. As mentioned above, the argument definition of the wrapped function must match inside and outside the decorator, hence a and b arguments are passed through wrapper function which is again passed through wrapped function inside the decorator. In this example, the wrapper function is defined to calculate the time taken by wrapped function to perform the execution of given task.

import time

def MyDecorator(funct):
def MyWrapperFunction(a, b):
start = time.time()
funct(a, b)
end = time.time()
# 1 is added because the execution time is very small.
print("Time taken by", funct.__name__, "is", 1+end-start)
return MyWrapperFunction

@MyDecorator
def MyFunction(a, b):
print("sum of", a, "and", b, "is", a+b, ".")

MyFunction(15, 10)


Output

sum of 15 and 10 is 25 .
Time taken by MyFunction is 1.000011


### Example: function with arguments and a return type

In the below example, wrapped function called MyFunction returns value. Hence, there must be a return statement inside the wrapper function to get return from wrapped function. Please note that, in this example, *arg, **kwarg parameters are used to match with the argument definition of wrapped function.

def MyDecorator(funct):
def MyWrapperFunction(*arg, **kwarg):
print("Before Execution.")
abc = funct(*arg, **kwarg)
print("After Execution.")
return abc
return MyWrapperFunction

@MyDecorator
def MyFunction(a, b, c):
print("Inside wrapped function.")
return a + b + c

x = MyFunction(15, 10, 5)
print("The sum is", x)


Output

Before Execution.
Inside wrapped function.
After Execution.
The sum is 30