15. 裝飾器
對於某些函式執行之前或之後要加入某些動作或檢查,但是又不修改那些函式的內容(讓動作或檢查可以容易的加入或移除),此時可以用裝飾器(decorator)完成此項工作。
(1) 將輸出的結果改為大寫
以下是一個例子:
def myDecorator(func):
def wrapper(*args, **kwargs):
r = func(*args, **kwargs)
if isinstance(r, str):
return r.upper()
else:
return r
return wrapper
@myDecorator
def myname(name):
return name
第11行用@myDecorator宣告一個裝飾器來修飾其後的myname函式,此時myname函式的參考將傳給第1行myDecorator的參數func,因此func指向myname函式物件。
第2行的wrapper函式宣告一個包裹myname的函式,在第3行先執行myname函式,取得回傳值後將它轉成大寫(對myname函式增加的動作)。
第9行回傳wrapper的參照並由第12行的myname接收,因此myname就指向了wrapper函式物件。以上完成了在不修改myname內容及參照名稱的情況下,增加了原有myname函式的處理。
主程式:
print(myname('tomlin'))
執行結果:
TOMLIN
說明

(2) 使用多個裝飾器
一個函式可以使用多個裝飾器修飾自己,修飾的順序是靠近自己的函式開始,一步步向外套用裝飾器。
以下是一個例子:
#第一個裝飾器
def myDecorator1(func):
def wrapper(*args, **kwargs):
r = func(*args, **kwargs)
if isinstance(r, str):
return r.upper()
else:
return r
return wrapper
#第二個裝飾器
def myDecorator2(func):
def wrapper(*args, **kwargs):
r = func(*args, **kwargs)
return 'abc_' + r + '_xyz'
return wrapper
# 使用裝飾器
@myDecorator1
@myDecorator2
def myname(name):
return name
第22行的myDecorator2先修飾myname,在回傳值前後加入abc_及_def 文字;接著再套用myDecorator1修飾,將原本文字及新增的文字都轉成大寫。
主程式:
print(myname('tomlin'))
執行結果:
ABC_TOMLIN_XYZ
如果改變裝飾順序:
@myDecorator2
@myDecorator1
def myname(name):
return name
執行結果:
abc_TOMLIN_xyz
說明:

(3) 接收參數的裝飾器
以下是一個例子:
#接收參數的裝飾器
def myDecorator(chineseUpper):
def innerDecorator(func):
def wrapper(*args, **kwargs):
up = '零壹貳參肆伍陸柒捌玖'
lw = '〇一二三四五六七八九'
r = func(*args, **kwargs)
m = ''
if chineseUpper:
for k in str(r):
m+=up[int(k)]
else:
for k in str(r):
m+=lw[int(k)]
return m
return wrapper
return innerDecorator
# 使用裝飾器並傳參數
@myDecorator(False)
def total(price, amount):
tot = price*amount
if tot>100000:
tot*=0.8
return int(tot)
主程式:
print(total(1250, 85))
執行結果:
八五〇〇〇
說明:

問題1:
寫一個新台幣裝飾器,名稱是NTdecorator,負責將被修飾函式計算後的數字轉成國字大寫,如12345轉成壹萬貳仟參百肆拾伍元。假設被修飾函式計算後的數字範圍介於0~1000000之間。
問題2:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
if __name__ == '__main__':
app.run()
測試以上程式,試著說明第5行指令的意義。
Last updated