Decorators in python
Tags: python, tutorial
Published: 2022-08-09 22:12:00

**Decorators: end you everlasting confusion about decorators and its uses**

Whenever going through github or any other codebase, we often come across somthing that looks unlike python.

```
@decorator
def my_function():
    pass
```
This is known as a decorator in python and is extremely handy and powerful. A decorator is used when we need to change the behaviour of a function without changing the function itself. Makes sense? It will now.
It can be said that _almost everything in python is an object_. Functions are also objects(first class). Therefore we can assign functions to a variable.

```
def my_function():
    print("hello dev community")

#we can assign the function without executing to the variable func
func=my_function
```
**Functions are first class objects**
They can be assigned to a varibale as shown earlier.
Moreover they can be passed as argument to another function and also nested into another function.

```
def outer(message):
    def inner():
        print("hi "+ message)
    return inner

hi = outer("dev community")
bye = outer("dev community")

#this executes the inner function.
hi()
bye()
```
**output**

```
hi dev community
bye dev community
```
Here we have two nested function `outer` and `inner`
`outer` returns `inner` without executing it. When we assign `outer` to a variable and execute the variable,it actually executes `inner` with the specific argument.
This is called a closure. Closures can avoid the use of global values and provides some form of data hiding. It can also provide an object oriented solution to the problem.

**Decorators extensively use closures**
Rather than taking `message` as an argument decorators take functions as arguments.Let's create a decorator step-by-step.

```
def decorator_function(original_function):
    def wrapper_function():
        #do something before the function
        print("hello from wrapper")
        return original_function()
    return wrapper_function

def display():
    print("hello from display")

decor_display = decorator_function(display)
decor_display()
```
The code looks extremly similar to the closure we created earlier. We are now passing a function through `decorated_function`.

When we assign `decor_display` the `decorator_function` executes and returns `wrapper_function`. We then execute the `wrapper_function` stored in `decor_display` by invoking `decor_display` in the last line. Now the `wrapper_function` executes its print statement and the `original_function` which in our context is `display` and finally outputs:

```
hello from wrapper
hello from display
```
That was a lot to take in for sure.Go through the process again and  hopefully the logic will be clear. 
Here we successfully managed to changed the behaviour of the `display` function by printing and extra line before it `hello from wrapper`. We did not change the `display` function itself but rather changed its behaviour using our `decorator_function`.

Python allows us to do this same thing using `@decorator_function_name`. Rather than assigning `decorator_function` to a variable we can assign it to any function using `@decorator_function` before the function.

```
def decorator_function(original_function):
    def wrapper_function():
        #do something before the function
        print("hello from wrapper")
        return original_function()
    return wrapper_function


#this works same as decorator_function(display)
@decorator_function
def display():
    print("hello from display")

display()
```
**output**

```
hello from wrapper
hello from display
```

**Conclusions**
Decorators are a powerful tool and is used in many codebases for various purposes such as  logging, performance testing, perform caching, verifying permissions etc. If you find this tutorial useful do comment below. Thank you for reading and learning.

[decorators part II](https://dev.to/xzm27/decorators-part-ii-dmo)->