# What is decorators in python
Decorators are used to add functionalities to an object without changing the structure of the object
## Let's see how a function looks like
```python=
def add_one(no):
return no + 1
num = add_one
num(5)
```
> Output: 6
## Let's see an example of Inner/nested function 🤔
```python=
def add_one(no):
def add_two(no):
return no + 2
return add_two(no)
print(add_one(4))
```
> Output: 6
## Let's see what will happen if we pass function as a parameter to a different function
```python=
def add_one(number):
return number + 1
def fun(function):
to_add = 5
return function(to_add)
print(fun(add_one))
```
> Output: 6
we have created two function where `fun` can have function as parameter and we are passing `add_one` as a function parameter to `fun` function
then we are passing the `to_add` value to the `add_one`
function
Now you're getting, how we are going to get to decorators.
## We can also return functions from functions
```python=
def parent(num):
def first_child():
return "It's Itachi"
def second_child():
return "I'm Sasuke"
if num == 1:
print(first_child())
return first_child
else:
print(second_child())
return second_child
first = parent(1)
second = parent(2)
print(first)
print(second)
```
> It's Itachi
> I'm Sasuke
> <function parent.<locals>.first_child at 0x7f6e5c7ff820>
> <function parent.<locals>.second_child at 0x7f6e5c7ff940>
```python=
first()
#>>> It's Itachi
second()
#>>> I'm Sasuke
```
Note: we are returning `first_child` without parentheses. this means we are returning a reference to the function `first_child`. Where `first_child()` with parentheses returns the result of the function.
Python also allows to use functions as return values.
> ### **This is why functions are called First-Class Objects**
functions can be passed around and used as arguments, just like any other object (str, int, bool, list, etc).
## What is `Closure`
not closer from chainsmokers 😅
```python=
def print_message(message):
"Outer Function"
def send_message():
"Nested Function"
print(message)
send_message()
print_message("Some message")
```
> Output: Some message
# @Decorators
First using normal function
```python=
def uppercase_decorator(function):
def wrapper():
func = function()
make_uppercase = func.upper()
return make_uppercase
return wrapper
def hello():
return "Hey there"
decorate = uppercase_decorator(hello)
print(decorate())
```
> Output: HEY THERE
Now same thing using decorator
```python=
def uppercase_decorator(function):
def wrapper():
func = function()
make_uppercase = func.upper()
return make_uppercase
return wrapper
@uppercase_decorator
def hello():
return "using decorator"
print(hello())
```
> Output: USING DECORATOR
To debug decorators we can use functools
```python=
import functools
def uppercase_decorator(func):
@functools.wraps(func)
def wrapper():
return func().upper()
return wrapper
@uppercase_decorator
def say_hi():
"This will say hi"
return 'hello there'
print(say_hi())
print(say_hi.__name__)
print(say_hi.__doc__)
```
> Output: HELLO THERE
> say_hi
> This will say hi
###### tags: `python` `decorators` `interview`