# Decorator
Decorator 基本上是用另一個函式來包裝目的函式以增強或限制目的函式的功能
範例
```python3=
def my_deco(f): #decorator的外包裝,用以承接要裝飾的函式
def uppercase(*args, **kvargs):
#decorator真正作用的內容, 希望用目的函式中的參數做的事情
#利用*args 與 **kvargs來容許多個參數與多個KV對
return f(*args).upper() #執行完f(n)再帶入.upper()
return uppercase #回傳裝飾完的結果
@my_deco #利用@+裝飾器名來裝飾下方函式
def breakfast(food, drink):
return f"Hi, I want to have {food} for breakfast with a cup of {drink}. Thank you!"
print(breakfast("toast", "milk"))
#其實在上方加@my_deco 的寫法等同於
decorated_breakfast = my_deco(breakfast) #定義decorator
print(decorated_breakfast("toast", "milk"))
```
利用 functools 中的wraps函數來避免原本的函式被裝飾器改寫
用法為@wraps加在裝飾器的上一行
```python3=
from functools import warps
def decorator_name(f)
@wraps(f)
decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return decorated
@decorator_name
def func():
return ("Function is running")
can_run = True
print(func()) #Function is running
can_run = False
print(func()) #Function will not run
```
## 應用
- Speed Test
```python=
from functools import wraps
from time import time
def speedtest(fx):
@wraps(fx)
def timecount(*args, **kvargs):
start_time = time()
result = fx(*args, **kvargs)
end_time = time()
print(f"Now executing {fx.__name__}")
print(f"Tatal running time ={end_time-start_time}")
print(result)
return result
return timecount
@speedtest
def sum_in_generator():
return sum(x for x in range(1,100000000))
@speedtest
def sum_in_list():
return sum([x for x in range(1,100000000)])
sum_in_generator()
sum_in_list()
#Now executing sum_in_generator
#Tatal running time =7.44240403175354
#4999999950000000
#Now executing sum_in_list
#Tatal running time =29.27103590965271
#4999999950000000
```
- 用decorator來強制轉換輸入的參數類型
因為要指定須要轉換的類型,所以要在最外圍多加一個函式來decorator可以接受變數(資料型態)
```python=
from functools import wraps
def changer(*types):#decorator的最外層,承接要定義的types
def deco(fx):#內部decorator層承接輸入的函式
def trans(*args, **kvargs):#轉換數值的函式,承接要轉換的值
new_args = []#先建立一個空的list來裝轉換型態後的值
for (i,j) in zip(args, types):#把每一對數值跟資料型態用tuple結合起來
new_args.append(j(i))
return fx(*new_args, **kvargs)#回傳內含轉換好的值的函式
return trans #用trans回傳函式計算的結果
return deco #最後整個changer輸出deco的結果
@changer(str,int)
def introduction(name, age):
for i in range(age):
print(f"Hi, my name is {name} and I'm {i} years old")
introduction("Apple", "5")#原本的5雖然是str,但用decorator轉換成int便可以進入iteration
#Hi, my name is Apple and I'm 0 years old
#Hi, my name is Apple and I'm 1 years old
#Hi, my name is Apple and I'm 2 years old
#Hi, my name is Apple and I'm 3 years old
#Hi, my name is Apple and I'm 4 years old
```
- 檢查授權
```python=
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.passord):
authenticate()
return f(*args, **kwargs)
return decorated
```