# How to implement custom Decorator in Python

### Introduction to Decorator design-pattern

A decorator pattern is a well-known structural design pattern used to add new behavior to an existing object dynamically without altering already existing behavior. Decorator works by placing the object inside wrapper objects to add the functionality of the wrapper objects to the wrapped object.

By using decorator pattern, you can achieve the Signle-Responsibilty Principle by dividing the responsibilities among different functions. It also helps to maintain Open-Close Principle as you can add any new functionality to a new object without modifying the existing object's functionality.

### Simplifying decorator pattern in python with examples

```python
def func_a(arg):
    print("I am from A with argumnet "+ arg)

def decorator(func, arg):
    func(arg)
    
main(func_a, "C")
```

```bash
I am from A with argumnet C
```

In the above example, the **decorator** function is working as a higher-order function which is taking a function as an argument and calling it. Right now it's not doing any other task. Let's assign a task to the decorator function.

```python
def func_a(arg):
    print("I am from A with argumnet "+ arg)

def decorator(func, arg):
    print("I am in decorator with argument " + arg)
    func(arg)
```

```python
func_a("D")
```

```bash
I am from A with argumnet D
```

In the above example, I'm calling the function and it's printing the line mentioned above as expected.

```python
decorator(func_a, "C")
```

```plaintext
I am in decorator with argumnet C
I am from A with argumnet C
```

Now I've added additional behavior to the existing `func_a` by adding it inside a decorator without changing its existing behavior.

### Understanding Decorators in python

Now Let's come to Python decorators

```python
def decorator(func):
    def helper(arg):
        print("I am in decorator with argumnet " + arg)
        func(arg)
    return helper

def func_a(arg):
    print("I am from A with argumnet "+ arg)
```

```python
decorated_func = decorator(func_a)
decorated_func("C")
```

```bash
I am in decorator with argumnet C
I am from A with argumnet C
```

### The '@' syntax of python Decorators

The above code in python can also be written like

```python
def decorator(func):
    def helper(arg):
        print("I am in decorator with argumnet " + arg)
        func(arg)
    return helper

@decorator
def func_a(arg):
    print("I am from A with argumnet "+ arg)
```

```python
fun_a("C")
```

```bash
I am in decorator with argumnet C
I am from A with argumnet C
```

The "@" symbol in python is shorthand for writing decorators.

Let's go with a more realistic example where we can use a decorator effectively

```python
def hanle_index_exception(func):
    def helper(arr, pos):
        if pos >= len(arr):
            return "position is greater that list length."
        return func(arr, pos)
    return helper

@hanle_index_exception
def value_at_position(arr , pos):
    return arr[pos]

arr = [1, 3, 5, 7]
val_1 = value_at_position(arr, 2)
print(val_1)
val_2 = value_at_position(arr, 4)
print(val_2)
```

```bash
5
position is greater that list length.
```

In the above example, we're using a decorator to handle index out-of-bound exceptions.

### Chaining of decorators

Multiple decorators can also be chained in python means decorating a function multiple times.

Here is a simple example:

```python
def remove_whitespaces(func):
    def hepler(name):
        name = name.strip()
        func(name)
    return hepler

def format(func):
    def hepler(name):
        name = name.capitalize()
        func(name)
    return hepler

@remove_whitespaces
@format
def print_name(name):
    print(name)
    
print_name('    akash  ')
```

```bash
Akash
```

In the above example, **remove\_whitespaces decorator** removes the whitespaces first. Then **format** decorator made the first letter capitalized. You should maintain the order correctly when adding more than one decorator as the topmost one will be applied first.

I hope now we have a better understanding of how to use decorator in python, you can easily use some decorator in your next project.

Happy Learning !!
