python 的函數裝飾器Function decorator)
對一個方法應用多個裝飾方法:
@A
@B
@C
def f ():
#等價於下面的形式,Python會按照應用次序依次調用裝飾方法(最近的先調用)
def f():
f = A(B(C(f)))
裝飾方法解析:
每個decorator只是一個方法, 可以是自訂的或者內建的(如內建的@staticmethod/@classmethod)。decorator方法把要裝飾的方法作為輸入參數,在函數體內可以進行任意的操作(可以想象其中蘊含的威力強大,會有很多應用情境), 只要確保最後返回一個可執行檔函數即可(可以是原來的輸入參數函數, 或者是一個新函數)。decorator的作用對象可以是模組層級的方法或者類方法。decorator根據應用時的參數個數不同分為兩類:無參數 decorator,有參數decorator。下面分別介紹。
無參數decorator:
def deco(func):
"""無參數調用decorator聲明時必須有一個參數,這個參數將接收要裝飾的方法"""
print"Enter decorator" #進行額外操作
func.attr ='decorated' #對函數進行操作,增加一個函數屬性
return func #返回一個可調用對象(此例還是返回作為輸入參數的方法)
#返回一個新函數時,新函數可以是一個全域方法或者decorator函數的內嵌函數,
#只要函數的簽名和被裝飾的函數相同
@deco
def MyFunc(): #應用@deco修飾的方法
print"Enter MyFunc"
MyFunc() #調用被裝飾的函數
注意:當使用上述方法定義一個decorator方法時,函數體內的額外操作只在被裝飾的函數首次調用時執行,如果要保證額外操作在每次調用被裝飾的函數時都執行,需要換成如下的寫法:
def deco(func):
def replaceFunc(): #定義一個內嵌函數,此函數封裝了被裝飾的函數,並提供額外操作的代碼
print"Enter decorator" #進行額外操作
return func() #產生對被裝飾函數的調用
return replaceFunc #由於返回的是這個新的內嵌函數,所以確保額外操作每次調用得以運行
@deco
def MyFunc(): #應用@deco修飾的方法
print"Enter MyFunc"
MyFunc() #調用被裝飾的函數
有參數decorator:
def decoWithArgs(arg):
"""由於有參數的decorator函數在調用時只會使用應用時的參數而不接收被裝飾的函數做為參數,
所以必須返回一個decorator函數, 由它對被裝飾的函數進行封裝處理"""
def newDeco(func): #定義一個新的decorator函數
def replaceFunc(): #在decorator函數裡面再定義一個內嵌函數,由它封裝具體的操作
print"Enter decorator" #進行額外操作
return func() #對被裝飾函數進行調用
return replaceFunc
return newDeco #返回一個新的decorator函數
@decoWithArgs("demo")
def MyFunc(): #應用@decoWithArgs修飾的方法
print"Enter MyFunc"
MyFunc() #調用被裝飾的函數
當我們對某個方法應用了裝飾方法後, 其實就改變了被裝飾函數名稱所引用的函數代碼塊進入點,使其重新指向了由裝飾方法所返回的函數進入點。由此我們可以用decorator改變某個原有函數的功能,添加各種操作,或者完全改變原有實現。
例子 11.2 使用函數裝飾器的例子(deco.py)
這個裝飾器(以及閉包)示範表明裝飾器僅僅是用來“裝飾“ (或者修飾)函數的封裝,返回一
個修改後的函數對象,將其重新賦值原來的標識符,並永久失去對原始函數對象的訪問。
1 #!/usr/bin/env python
2
3 from time import ctime, sleep
4
5 def tsfunc(func):
6 def wrappedFunc():
7 print '[%s] %s() called' % (
8 ctime(), func.__name__)
9 return func()
10 return wrappedFunc
11
12 @tsfunc
13 def foo():
14 pass
15
16 foo()
17 sleep(4)
18
19 for i in range(2):
20 sleep(1)
21 foo()
運行指令碼,我們得到如下輸出:
[Sun Mar 19 22:50:28 2006] foo() called
[Sun Mar 19 22:50:33 2006] foo() called
[Sun Mar 19 22:50:34 2006] foo() called
參考:
http://www.cnblogs.com/jifangliang/archive/2008/07/22/1248313.html
http://www.x5dj.com/Blog/00182202/00574182.shtml
http://hi.baidu.com/cricstiano/blog/item/83f9ce732dad7f1b8701b0f3.html