# Python Decorators

Hello guys👋👋!! Welcome back to my second blogpost. In this post, we will be looking at Python Decorators along with some real-life examples. It took me a while to understand the concept of decorators. So, I thought of writing a blogpost which will make it easier to understand. Without further ado, let's jump right in!

![minions-strong.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634135235865/CWuemOyFb.gif)

# Python Decorators: - What is that???🤔

> 
A decorator is a function that takes another function and adds some functionality to it, without modifying it.

Don't worry, if you don't get it right away😕. It's always good to break a difficult concept into smaller pieces and connecting those pieces into that whole concept for better understanding. Let's break the decorators into pieces.

Before trying to understand decorators, we must know about the functions. Let's talk about the **BIG 3** about functions.


![the-falcon-and-the-winter-soldier-tfatws.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634910461421/MkzmYtZadz.gif)

- 
The first-class objects

- 
Inner functions

- 
Returning functions from functions

--------

## The first class objects:😎
Python functions are **First-class citizens**, meaning they can be passed as arguments, modified and can be assigned to a variable as any other python objects (ie., list, string, int..)

```
def my_name(name):
  return f'Hello {name}'


greet = my_name
greet('Python')

``` 
**Output:**

```
Hello Python
``` 


**Explanation:**

What is happening in that above block of code? First, we have assigned a variable `greet` to the defined function `my_name`. Then, we have passed an argument (value) Python to the variable `greet` which calls the function `my_name`.

This is the Python's first class objects. We can store the function in a variable, can store them in data structures such as lists, dictionaries., can be passed as a function and so on..

--------------

## Inner Functions:🙂
A function defined within another function. The order of function does not matter. The inner functions will only be defined when parent function is called. The inner functions are of local scope within parent functions.
They are also called as **nested functions**.


```
def my_name(name):
  print f'My name is {name}'
  def first_name(first):
    print f'My first name is {first}'
  def last_name(last):
    print f'My last name is {last}'
    
    first_name('Python')
    last_name('Programming')


my_name('Python Programming')

``` 

**Output:**


```
My name is Python Programming
My first name is Python
My last name is Programming

``` 

**Explanation:**

In the above code snippet, we are defining a function `my_name`, which has two inner functions within it - `first_name` and `last_name`. The first_name and last_name scope are within the Parent function `(my_name)`.

------------------

## Returning Functions from Functions:🙃

We saw that Python functions are first-class objects. That is, they can be passed as arguments and can also be returned as a value from another function.
Let's see it in action.


```
def my_name():
  def first_name(name):
    return f'My first name is {name}'
  return first_name



greet = my_name()
greet('Python')

``` 

**Output:**


```
My first name is Python

``` 

**Explanation:**

When the PARENT function `my_name` is called, the child `first_name` is also being called. We are then returning back the function `first_name` as the value.

----------------

Now that the pre-requisites for decorators are complete, let's take a look at the glorious Python Decorators.

To Recall,

> 
A decorator is a function that takes another function and adds some functionality to it, without modifying it.

To demystify, let us make a sandwich. Uhm.., Yes, You heard it right. **A VEGETABLE SANDWICH!!**

### Cooking Class 101 - How to make a vegetable sandwich!🥪


![Sandwich.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634912407796/QgkpwpbZq.gif)

We will take some veggies, cut them in same size, arrange them neatly on top of bread, add some cheese slices and add another bread at the top of cheese slices.


> 
Note:
Here we are just adding layers as per our taste. We are not changing any function of the individual elements.
> 


Pretty Easy right. The same with Decorators. You take a function, add some layers to it without modifying the original function or structure.
Now, take a look at the below code.


```
#Decorator Declaration
def my_decorator(func):
  def wrapper():
    print "Before Function call"
    func()
    print "After Function call"
 	return wrapper

#Function Declaration
def my_name():
  print("My name is Python")


#Function call
my_name = my_decorator(my_name)
my_name()

``` 

**Code Explanation:**

1. Take a look at the first part - ** *Decorator Declaration* **

It is nothing but a function which has an inner function - `wrapper() ` within it.

2. Now Second part - ** *Function Declaration* **

This is where we declare our main function, to which you need to add extra layers to extend its functionality.

3. Now the Last part - ** *Function call* ** 

Here, we are assigning the function to the decorator and calling the decorator function with the function `my_name` as its argument.


> 
In the above code, wrapper is the part where we have to concentrate. It is where the layers are getting added.

- 
As the name implies, the decorator takes the function `my_name` as it argument.

- 
It executes it's inner function `wrapper`, which will print the statement - **"Before Function call"**

- 
Then, the `func()` will call the original function `my_name`, which prints -** "My name is Python"**

- 
Again it executes the last statement - **"After Function call"** before returning the wrapper.


**Output:**

```
Before Function call
My name is Python
After Function call

``` 

If the above explanation is still confusing, let us take a look at another example⬇️


![20KMhr.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634913530576/ua5aNIjm1.gif)


You are planning to go out on a bright day. Suddenly, *wind* of 25 km/hr blows. What would you do?
Add a *sweater* and carry on your work.
But, then again, it *rains*. It is really turning into a bad day, isn't it?
We would take an umbrella or wear a *rain coat* and carry on our work, because ***LIFE GOES ON!!***🥴

Whatever the situation, we would simply wrap ourselves with some function. That is decorator. It may be a sweater or a rain coat. Doesn't matter.

--------------------------

## Syntactic Sugar:

The python decorators can be simplified by using the Syntactic Sugar - ** *@ symbol, called as PIE Syntax* **

Instead of assigning the decorator to the variable `my_name` and function, we can simply use @ to call the decorator.


```
#Decorator Declaration
def my_decorator(func):
  def wrapper():
    print "Before Function call"
    func()
    print "After Function call"
 	return wrapper

#Function Declaration - Applying decorator using @ syntax

@my_decorator
def my_name():
  print("My name is Python")

#Function call
my_name()

``` 

----------------------

So far, we have been looking about decorators, but why use them in the first place? Why can't we simply use functions instead?

That is a very good question.

Some of the **Advantages** of Decorators include,

- 
can be reused anywhere in the program.

- 
Decorators can accept arguments, however wrapper function should use positional and keyword arguments (*args, **kwargs).

- 
Decorators can return values.


And most importantly, if you are someone who is planning to learn **Web development with FLASK (Python)**, decorators will be used in the program like many times. Therefore, it is safe to learn it beforehand.

---------------------

That's all I have for you guys in this blogpost. Thank you for making it so far. 
Take care and I will see you soon with another blogpost!!💙

[Also, Check out my** Blog website**, made using **Flask**❤️](https://akshaya-blog.herokuapp.com/) 

[I am on Twitter too!!](https://twitter.com/akshaya_lr) 

![good-bye-peace-out.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634914808871/DZWcFGFQe.gif)
