* 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) ```