# Decorators in Python
Decorator is a function, which modifies another function and returns it. The new function is often referred to as ==Decorated function==.
> Why to use it ?
Suppose we are passing our name as an argument to a function and we want a greeting before it, let's say Hello followed by the name, then we can simply modify the function and add a hello before it, that can be the easiest way. But in case when we are working on a big project, with many functions and we want a similar modification in all of it, then changing individual function would be a lot of task and it will also effect the readability. In such cases, using decorators is advised.
> Before getting into the syntax of Decorators, it is important to know about how a function can be used in many ways.
1. Functions can be assigned to variable

2. Functions can be passed as an argument

3. Functions can be returned from another function

### Syntax
Now as discussed above Decorators are used to modify an function. Lets get into the synatx of decorators with some coding examples. Suppose we want to add a greeting and a thank you test to an function :-
```python
def greet(func):
print("Hello")
func()
print("Thank You")
@greet
def hello():
print("viewer")
```
>This is same as writing the below code :-
```python
def greet(func):
print("Hello")
func()
print("Thank You")
def hello():
print("viewer")
greet(hello)
```
>in both the cases, the output will be the same.
**output**
Hello
viewer
Thank you
>In a more detailed manner, we can also modify the code to return a function that is the modified function.
```python
def greet(func):
def mfunc():
print("Hello")
func()
print("Thank You")
return mfunc
@greet
def hello():
print("viewer")
hello()
```
>In case we are passing arguments, we need to pass them in the following manner :-
```python
def greet(func):
def mfunc(*args, **kwargs):
print("Hello")
func(*args, **kwargs)
print("Thank You")
return mfunc
@greet
def hello(a, b):
print(a+b)
hello(5,10)
```
**Output**
Hello
15
Thank You
### Chaining Decorators
It simply means decorating a function with multiple decorators.
```python
def func1(func):
def inner():
x = func()
return x * x
return inner
def func2(func):
def inner():
x = func()
return 2 * x
return inner
@func1
@func2
def num1():
return 10
@func2
@func1
def num2():
return 10
print(num1())
print(num2())
```
**Output**
400
200
The above code can be seen as :- print(func1(func2(num))) and print(func2(func1(num))).
### Practical Uses
One common use of Decorators is to add logging to a function. For example, you can use a decorator to the arguments and return value of a function each time it is called.
### MCQ Questions
1. What symbol is used to decorate a function
- !
- &
- /
- @
2. Decorators modify a function
- Temporarily
- permanent
- can't be determined
- none of the above
3. Which of the following is true
- Functions can be assigned to a variable
- Functions can be passed as an argument
- Functions can be returned from another functions
- all of the above
4. What type of arguments are used to create general purpose decorators?
- Positional
- Arbitrary
- keyword
- default
5. Identify the decorator function in the below code:
```python
def greet(func):
print("Hello")
func()
print("Thank You")
@greet
def hello():
print("viewer")
```
- greet()
- hello()
6. Find the output of the below code below code:
```python
def greet(func):
print("Hello")
func()
print("Thank You")
@greet
def hello():
print("viewer")
```
- Hello
- Hello
Thank You
- Hello
viewer
Thank You
- error
7. which of the following statements are true:
statement-1: Nested functions are possible
statement-2: Multiple decorators for a function can exist
- Both statement -1 and 2 are true
- statement -1 is true, statement -2 is false
- statement -1 is falsr, statement -2 is true
- Both the statemnts-1 and 2 are false
8. What’s the primary purpose of decorators in Python?
- To change the return type of a function
- To optimize the performance of a function
- To modify the behavior of a function
- To create new functions dynamically
9. Which statements about the the variables referenced and evaluated are true?
```python
def reference():
def inner():
return 42
return inner
def evaluation():
def inner():
return 42
return inner()
referenced = reference()
evaluated = evaluation()
```
- There’s no difference between the two.
- The variable evaluated will be the integer 42.
- The variable referenced will be a reference to a function object.
- Neither referenced nor evaluated will hold the value 42.
10. What’s the purpose of the @ symbol when using decorators?
- It unlocks a library of built-in decorators.
- It increases code complexity.
- It simplifies applying a decorator to a function.
11. You’ve decorated a function that accepts an argument, but when running it, you get an error:
TypeError: wrapper() takes 0 positional arguments but 1 was given
What might be a good approach to try to fix this error?
- Using *args and **kwargs in the inner wrapper function
- Creating separate decorators for functions with and without arguments
- Removing the arguments from the function
12. Is it possible to define a decorator without an inner function?
- Yes
- No
13. Which of the following statements are true in regard to applying multiple decorators to a function?
- The order of decorators doesn’t matter.
- You can’t apply more than one decorator to a function.
- You can apply several decorators by stacking them on top of each other.
- The order of decorators matters.
14. How many functions should you define to create a decorator with arguments?
- One
- Two
- Four
- Three
15. Idenify the line with error:
```python
def greet(func):
print("Hello")
func()
print("Thank You")
!greet
def hello():
print("viewer")
```
- Line 1
- Line 3
- Line 5
- Line 6
16. Idenify the line with error:
```python
def greet(func):
print("Hello")
func()
print("Thank You")
@greet
def hello():
print("viewer")
```
- Line 1
- Line 3
- Line 5
- No error
17. Find the output of the following code
```python
def func1(func):
def inner():
x = func()
return 2 * x
return inner
def func2(func):
def inner():
x = func()
return x * x
return inner
@func1
@func2
def num1():
return 10
@func2
@func1
def num2():
return 10
print(num1())
print(num2())
```
- 200
400
- Error
- 400
200
18. Which of the statements correctly interprets the below code
```python
def func1(func):
def inner():
x = func()
return 2 * x
return inner
def func2(func):
def inner():
x = func()
return x * x
return inner
@func1
@func2
def num1():
return 10
print(num1())
````
- print(func2(func1(num1)))
- print(func2(num))
- print(func1(num))
- print(func1(func2(num1)))
19. The new function obtained after being modified by a decorator is termed as
- Decorator function
- Modified Function
- Decorated Function
- New Function
20. What will be the output of the following code?
```python
def create_adder(x):
def adder(y):
return x+y
return adder
add_15 = create_adder(15)
print(add_15(10))
```
- 15
- 10
- Error
- 25