---
# System prepended metadata

title: Python - Decorator
tags: [Python]

---

* Decorator is a Higher Order Function.
* Higher Order Function means a function that returns a function or take a function as an argument(or both).
* 懶人包：如果要對每個function做benchmarking(測時間)或是logging之類的統一動作，可以把code寫在裝飾器裡面，這樣呼叫function的同時就會先執行裝飾器，再執行function



### 1. Define a normal function:

```python
def original_func():
	print("The original function starts...")
	print("This function will return 2")
	print("The original function ends...")
	return 2
```


### 2. Defin a decorator:
```python
def decorator_func(function):
	def square():
		print("The decorated function starts...")
		print("This function will return the square of the number")
		print("The decorated function ends...")
		return function() ** 2

	return square        
```

### 3. Use the decorator:
```python

result = decorator_func(original_func)()
print(result)
```
> The decorated function starts...
This function will return the square of the number
The decorated function ends...
The original function starts...
This function will return 2
The original function ends...
4

### 4. Special syntax:
A easier way to invoke decorator_func(original_func).

By adding @decorator, we can just simply use the original function name to invoke a function instead of typing more words.
```python
def decorator_func(function):
	def square():
		print("The decorated function starts...")
		print("This function will return the square of the number")
		print("The decorated function ends...")
		return function() ** 2

	return square


@decorator_func
def original_func():
	print("The original function starts...")
	print("This function will return 2")
	print("The original function ends...")
	return 2

result = original_func()
print(result)
```


### 5. Application: Features Analysis

1. Create some fake data
```python=
import pandas as pd

d = {
	"feature_1": [1, 2, 3, 4, 5],
	"feature_2": [16, 28, 33, 45, 65]
}

fake_data = pd.DataFrame(d)
```
result:
```
   feature_1  feature_2
0          1         16
1          2         28
2          3         33
3          4         45
4          5         65
```

2. Create features
```python
# feature_3: feature_1 + feature_2
def generate_feature_3(data: pd.DataFrame) -> pd.DataFrame:
	data["feature_3"] = data["feature_1"] + data["feature_2"]
	return data


# feature_4: feature_1 / feature_2
def generate_feature_4(data: pd.DataFrame) -> pd.DataFrame:
	data["feature_4"] = data["feature_1"] / data["feature_2"]
	return data


features = fake_data.copy()

for generate_function in [generate_feature_3, generate_feature_4]:
	generate_function(features)

```
result:
```
   feature_1  feature_2  feature_3  feature_4
0          1         16         17   0.062500
1          2         28         30   0.071429
2          3         33         36   0.090909
3          4         45         49   0.088889
4          5         65         70   0.076923
```

If we want to increase/delete any feature generation function, it will become less efficient to mantain the code.
So, we could use "Decorator" to help.

```python
# revised with decorator
register_list = []


def register(function):
	register_list.append(function)
	print(f"REGISTER: {function}")
	return function


@ register
def generate_feature_3(data: pd.DataFrame) -> pd.DataFrame:
	data["feature_3"] = data["feature_1"] + data["feature_2"]
	return data

@register
def generate_feature_4(data: pd.DataFrame) -> pd.DataFrame:
	data["feature_4"] = data["feature_1"] / data["feature_2"]
	return data


features = fake_data.copy()

for generate_function in register_list:
	generate_function(features)

print(features)
```