Python provides us with many constructs for performing different tasks. While programming, sometimes we may need to modify the working of a function. But we may not be allowed to change the source code of the function as it might be in use in its original form in the program. In such cases, Python decorators can be used.

In this article, we will study what Python decorators are, how we can create decorators, and how we can use them to modify the functionalities of other functions in python.

## What is a Python decorator?

Python decorators are functions or other callable objects that can be used to add functionalities to another function without modifying its source code. A decorator in python accepts a function as an input argument, adds some functionalities to it and returns a new function with the modified functionalities.

Implementation of decorators in python requires knowledge of different concepts such as first class objects and nested functions. First, we will take a look at these concepts so that we do not face problems in understanding the implementation of python decorators.

## Concepts required to understand decorators

### First class objects

In Python, first class objects are those objects that

- can be passed to a function as a parameter.
- can be returned from a function.
- can be assigned to a variable.

All the variables that we use in our programs are first class objects, whether it be a primitive data type, a collection object or objects defined using classes.

Here I want to emphasize that **functions in python are also first class objects and we can pass a function as an input parameter or we can return a function from a function.**

For Example, let us look at the following source code.

Here, we have defined a function ** add()** that takes two numbers as input and prints their sum. We have defined another function

**() that takes a function as input, randomly generates two numbers and calls the input function**

*random_adder***() with the randomly generated numbers as input.**

*add*```
import random
def add(num1, num2):
value = num1 + num2
print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))
def random_adder(func):
val1 = random.randint(0, 10)
val2 = random.randint(0, 100)
print("In the random_adder. Values generated are {} and {}".format(val1, val2))
func(val1, val2)
# execute
random_adder(add)
```

Output:

```
In the random_adder. Values generated are 1 and 14
In the add() function. The sum of 1 and 14 is 15.
```

From the code and output, you can observe that the function ** add()** has been passed to the

**() function as input and the**

*random_adder***() function calls the**

*random_adder***function that prints the output.**

*add()*We can also return a function from another function or callable object. For instance, we can modify the above source code and define a function ** operate**() inside the

**() function. The**

*random_adder***() function performs the entire operation done by the**

*operate***() function in the previous source code.**

*random_adder*Now, we can return the ** operate**() function from the

**() function and assign it to a variable named**

*random_adder***. In this way, we will be able to execute the**

*do_something***() function outside the**

*operate***() function by calling the variable**

*random_adder***as follows.**

*do_something*```
import random
def add(num1, num2):
value = num1 + num2
print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))
def random_adder(func):
print("In the random_adder.")
def operate():
val1 = random.randint(0, 10)
val2 = random.randint(0, 100)
print("In the operate() function. Values generated are {} and {}".format(val1, val2))
func(val1, val2)
print("Returning the operate() function.")
return operate
# execute
do_something = random_adder(add)
do_something()
```

Output:

```
In the random_adder.
Returning the operate() function.
In the operate() function. Values generated are 3 and 25
In the add() function. The sum of 3 and 25 is 28.
```

### Nested Functions

Nested functions are the functions defined inside another function. For example, look at the following source code.

Here, we have defined a function ** add**() that takes two numbers as input and calculates their sum. Also, we have defined the function

**() inside**

*square***() that prints the square of the “**

*add***” calculated in the**

*value***() function.**

*add*```
def add(num1, num2):
value = num1 + num2
print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))
def square():
print("I am in square(). THe square of {} is {}.".format(value, value ** 2))
print("calling square() function inside add().")
square()
# execute
add(10, 20)
```

Output:

```
In the add() function. The sum of 10 and 20 is 30.
calling square() function inside add().
I am in square(). THe square of 30 is 900.
```

### Free Variables

We know that a variable can be accessed in the scope in which it has been defined. But, In the case of nested functions, we can access the elements of the enclosing function when we are in the inner function.

In the above example, you can see that we have defined the variable “** value**” inside the

**() function but we have accessed it in the**

*add***() function. These types of variables are called free variables.**

*square***But why the name free variables?**

Because it can be accessed even if the function in which it has been defined has completed its execution. For example, look at the source code given below.

```
def add(num1, num2):
value = num1 + num2
print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))
def square():
print("I am in square(). THe square of {} is {}.".format(value, value ** 2))
print("returning square() function.")
return square
# execute
do_something = add(10, 20)
print("In the outer scope. Calling do_something.")
do_something()
```

Output:

```
In the add() function. The sum of 10 and 20 is 30.
returning square() function.
In the outer scope. Calling do_something.
I am in square(). THe square of 30 is 900.
```

Here, Once the ** add**() function returns the

**() function, it completes its execution and is cleared from the memory. Still, we can access the variable “**

*square***” by calling the**

*value***() function that has been assigned to the variable**

*square***.**

*do_something*Now that we have discussed the concepts needed for implementing python decorators, Let’s dive deep and look how we can implement decorators.

## How to create Python Decorators?

We can create python decorators using any callable object that can accept a callable object as input argument and can return a callable object. Here, we will create decorators using functions in Python.

For a function to be a decorator, it should follow the following properties.

- It must accept a function as an input.
- It must contain a nested function.
- It must return a function.

First, we will define a function ** add**() that takes two numbers as input and prints their sum.

```
def add(num1, num2):
value = num1 + num2
print("The sum of {} and {} is {}.".format(num1, num2, value))
# execute
add(10, 20)
```

Output:

`The sum of 10 and 20 is 30.`

Now, we have to define a decorator in such a way that the ** add**() function should also print the product of the numbers along with the sum. For this, we can create a decorator function.

Let us first define a function that takes the ** add**() function as input and decorates it with the additional requirements.

```
def decorator_function(func):
def inner_function(*args):
product = args[0] * args[1]
print("Product of {} and {} is {} ".format(args[0], args[1], product))
func(args[0], args[1])
return inner_function
```

Inside the ** decorator_function**(), we have defined the

**() that prints the product of the numbers that are given as input and then calls the**

*inner_function***() function. The**

*add***() returns the**

*decorator_function***().**

*inner_function*Now that we have defined the ** decorator_function**() and the

**() function, let us see how we can decorate the**

*add***() function using the**

*add***().**

*decorator_function*### Create Python Decorators By passing functions as arguments to another function

The first way to decorate the ** add**() function is by passing it as an input argument to the

**(). Once the**

*decorator_function***() is called, it will return**

*decorator_function***() that will be assigned to the variable**

*inner_function***. After that, the variable**

*do_something***will become callable and will execute the code inside the**

*do_something***() when called. Thus, we can call**

*inner_function***to print the product and the sum of the input numbers.**

*do_something*```
def add(num1, num2):
value = num1 + num2
print("The sum of {} and {} is {}.".format(num1, num2, value))
def decorator_function(func):
def inner_function(*args):
product = args[0] * args[1]
print("Product of {} and {} is {} ".format(args[0], args[1], product))
func(args[0], args[1])
return inner_function
# execute
do_something = decorator_function(add)
do_something(10, 20)
```

Output:

```
Product of 10 and 20 is 200
The sum of 10 and 20 is 30.
```

### Create Python Decorators Using @ sign

A simpler way to perform the same operation is by using the “@” sign. We can specify the name of the ** decorator_function** after the

**sign before the definition of the**

*@***() function. After this, whenever the**

*add***() function is called, It will always print both the product and sum of the input numbers.**

*add*```
def decorator_function(func):
def inner_function(*args):
product = args[0] * args[1]
print("Product of {} and {} is {} ".format(args[0], args[1], product))
return func(args[0], args[1])
return inner_function
@decorator_function
def add(num1, num2):
value = num1 + num2
print("The sum of {} and {} is {}.".format(num1, num2, value))
# execute
add(10, 20)
```

Output:

```
Product of 10 and 20 is 200
The sum of 10 and 20 is 30.
```

In this method, there is a drawback that you cannot use the ** add**() function to just add the numbers. It will always print the product of the numbers along with their sum. So, choose the methodology by which you are going to implement the decorators by properly analyzing your needs.

## Conclusion

In this article, we have discussed what python decorators are and how we can implement them using functions in Python. To learn more about python programming, you can read this article on list comprehension. You may also like this article on the linked list in Python.

## Recommended Python Training

Course: Python 3 For Beginners

Over 15 hours of video content with guided instruction for beginners. Learn how to create real world applications and master the basics.