Python中函數有一個裝飾器的概念,今天,看核心編程中的函數一章的時候接觸到了這個概念,炸一看來,講的說明真實不好明白。於是寫下本篇以示說明,提供給迷糊者。希望能對一些人起到一定的幫助
裝飾器的語法以@開頭,接着是裝飾器要裝飾的函數的申明等。
其實總體說起來,裝飾器其實也就是一個函數,一個用來包裝函數的函數,裝飾器在函數申明完成的時候被調用,調用之後申明的函數被換成一個被裝飾器裝飾過後的函數。
裝飾器分爲無參裝飾和有參裝飾
無參裝飾很簡單
定義方法如下:
比如先定義一個裝飾方法:
def FirstDeco(func):
print '第一個裝飾器'
return func
@FirstDeco
def test():
print 'asdf'
....
申明完成之後顯示
...
第一個裝飾器
可見裝飾器在函數定義完成的時候被觸發
然後,咱們運行test
獲得asdf
多參裝飾:
多參裝飾複雜一點,多參裝飾的時候,裝飾函數先處理參數,再生成一個新的裝飾器函數,然後對函數進行裝飾
具體代碼如下:
>>> def deco(x):
... print '%s 開始新裝飾'
... def newDeco(func):
... def test(a,b):
... print 'begin'
... returnv = func(a,b)
... print 'end'
... return returnv
... return test
... return newDeco
...
這裏定義了一個裝飾其函數deco,裏面有一個參數x,這個時候,我們沒有直接使用func作爲裝飾函數的參數,而是隻用了參數x作爲參數,之後定義一個新的裝飾函數,newdeco,該函數才裝飾
然後定義如下:
>>> @deco(3)
... def mytest(x,y):
... if x>y:
... print x
... else:
... print y
...
%s 開始新裝飾
運行之後的結果爲
%s 開始新裝飾
>>> mytest(3,4)
begin
4
end
參考資料:
裝飾方法的產生:Python2.2通過增加靜態方法和類方法擴展了Python的對象模型。但是當時沒有提供一個簡化的語法去定義static/class方法,只得在定義好的方法尾部去調用staticmethod()/classmethod()方法達到目的。例如:
class C:def meth (cls):
meth = classmethod(meth) # 使meth方法成爲類方法
但是這樣會造成一個問題:當一個方法比較長時,很容易忘記尾部的調用。爲了簡化這個操作一個新的語法被加了進來:方法裝飾,以@開頭後跟裝飾方法 名,如@staticmethod/@classmethod,由此產生出decorator方法及decorator模式。現在我們可以這樣寫:
class C:@classmethod
def meth (cls):
可以對一個方法應用多個裝飾方法:
@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改變某個原有函數的功能,添加各種操作,或者完全改變原有實現。