裝飾器 decorator
或者稱為包裝器,是對函數的一種包裝。
它能使函數的功能得到擴充,而同時不用修改函數本身的代碼。
它能夠增加函數執行前、執行后的行為,而不需對調用函數的代碼做任何改變。
下面用一個簡單的例子介紹裝飾器:
1 # 函數hello,輸出 hello + name 的字符串 2 def hello(name): 3 return 'hello ' + name
下面,我們希望在每一個調用 hello 函數的時候,將輸出的字符串用 <tag>包住
比如:hello john 變成 <tag>hello john<tag>
方法1:自定義wrapper 函數
1 def wrapper(tag, func, *arg, **kvargs): 2 tag = "<" + tag + ">" #根據參數生成 <tag> 3 return tag + func(*arg, **kvargs) + tag #將<tag>加在func結果的首末 4 5 if __name__ == "__main__": 6 wrapper('p', hello, 'john') #輸出: <p>hello john<p>
這種方法成功修改了函數 hello 的行為,不過需要修改對 hello的調用。
每一個調用hello 的地方,都要給成調用wrapper,並修改參數列表
方法2:自定義decorator 函數
為了不改變對 hello的調用。我們需要得到一個新的函數對象,它修改 hello的行為,並用這個對象對 hello賦值。
從而調用 hello的時候,調用的是擴充行為后的 hello
1 def myDecorator(func, tag) 2 def myWrapper(*arg, **kvargs): #重新包裝func,其參數列表與func一致 3 sign = "<" + tag + ">" 4 return sign + func(*arg, **kvargs) + sign 5 return wrapper2 #調用 myDecorator 能返回一個函數對象,用於給func重新賦值 6 7 hello = myDecorator(hello, "div") #用新的函數對象修改hello 8 9 if __name__ == "__main__": 10 hello("john") #輸出 <div>hello john<div>
這樣,只要hello被myDecorator 賦值一次,以后再調用hello 時,就調用的是包裝后的函數
方式3:python的 decorator
python 的裝飾器所做的事與方式2類似
它通過語法糖使裝飾器看起來更清晰、簡介,而不用每次都書寫方式2中第7行代碼 hello = myDecorator(hello, "div")
1 def setTag(tag): #由於此裝飾器需要參數,所以要再套一層 2 def myDecorator(func): #裝飾器的核心,接受函數對象做參數,返回包裝后的函數對象 3 def myWrapper(*arg, **kvargs): #包裝的具體過程 4 sign = "<" + tag + ">" 5 return sign + func(*arg, **kvargs) + sign 6 return myWrapper 7 return myDecorator 8 9 @setTag("div") #用@標簽在定義函數時套上裝飾器 10 def hello(name): 11 return 'hello' + name
本質上,方式2 與 方式3 完成的是同一件事,只不過方式3 比方式2 代碼更簡潔,方便。
比如,現在要給 hello 函數套上三個標簽<body><div><p>
如果用方式2
hello = myDecorator(myDecorator(myDecorator(hello, "body"),"div"),"p")
如果用方式3
@myDecorator("body") @myDecorator("div") @myDecorator("p") def hello(name) return 'hello' + name
在多個裝飾器嵌套的情況下,python內置的decorator 結構更清晰。
裝飾器語法總結
完成一個裝飾器需要兩步:1.定義裝飾器函數 2.在被裝飾的函數定義之前加上 @裝飾器
下面的偽代碼展示了這個過程
1 def myDecorator(...): #定義裝飾器,可能帶參數 2 def decorator(func): #裝飾器核心,以被裝飾的函數對象為參數,返回裝飾后的函數對象 3 def wrapper(*args, **kvargs): #裝飾的過程,參數列表適應不同參數的函數 4 ... #修改函數調用前的行為 5 func(*args, **kvargs) #調用函數 6 ... #修改函數調用后的行為 7 return wrapper 8 return decorator 9 10 @myDecorator(...): #給函數加上裝飾器 11 def myFunc(...): #自己定義的功能函數 12 ...
參考:
[1] The Code Ship 中關於python裝飾器的說明:http://thecodeship.com/patterns/guide-to-python-function-decorators/
[2] python 裝飾器的主要用途與例子:https://wiki.python.org/moin/PythonDecoratorLibrary