Python有許多出色的語言特性,裝飾器(Decorator)即是其中一朵奇葩。先來看看一段代碼:python
def deco1(f): print 'decorate 1' return f def deco2(f): print 'decorate 2' return f
@deco1
@deco2 def foo(): return 'hello'
保存並執行上面的代碼,你會看到以下輸出:函數
decorate 2
decorate 1
函數foo沒有被調用,可是deco1,deco2被按照一個順序被調用了。deco1和deco2就是裝飾器。從上面的輸出能夠看出兩點:spa
1 裝飾器是在代碼裝載時被調用的code
2 調用的順是從下到上blog
如今來講說裝配器到底作了什麼。看代碼:源碼
def deco1(f): print 'decorate 1',f return f def deco2(f): print 'decorate 2',f return f @deco1 @deco2 def foo(): return 'hello'
這段代碼的執行結果爲:io
decorate 2 <function foo at 0x00000000021F79E8>
decorate 1 <function foo at 0x00000000021F79E8>
裝飾器函數的參數‘f’被打印出來,並且就是咱們的函數‘foo’,可見裝飾器的參數就是被裝飾的函數。真的是這樣嗎?再來看代碼:function
def wraper(n,f): def foo1(): return '%s(%s)'%(n,f())return foo1 def deco1(f): print 'decorate 1',f return wraper('decorate 1',f) def deco2(f): print 'decorate 2',f return wraper('decorate 2',f) @deco1 @deco2 def foo(): return 'hello'
這段代碼的執行結果爲:class
decorate 2 <function foo at 0x0000000002147A58>
decorate 1 <function foo1 at 0x0000000002147AC8>
我以爲我如今能夠總結一下了:語法
1 裝飾器在代碼裝載時被調用;
2 調用順序是從下到上的;
3 被裝飾函數‘foo’做爲參數傳遞給第一個裝飾器‘deco2’,返回值將做爲參數傳遞給第二個裝飾器‘deco1’,而後依次向上直到最頂端的裝飾器;
4 最頂端的裝飾器的返回值就是被裝飾之後的函數,咱們暫時稱之爲,也就是咱們未來要執行的那個‘foo’。
下面,來咱們說說裝飾後的函數,被調用會有什麼結果,看代碼:
def wraper(n,f): def foo1(): return '%s(%s)'%(n,f()) return foo1 def deco1(f): print 'decorate 1',f return wraper('decorate 1',f) def deco2(f): print 'decorate 2',f return wraper('decorate 2',f) @deco1 @deco2 def foo(): return 'hello' print foo()
這段代碼的執行結果爲:
decorate 2 <function foo at 0x0000000002127A58> decorate 1 <function foo1 at 0x0000000002127AC8> decorate 1(decorate 2(hello))
不難看出,調用順序與裝飾順序恰好相反,最終函數也就是最頂端的裝飾器返回的函數最早被調用,而後依次調用到最初那個函數‘foo’。我在這裏故意規避了一個基本的事實,就是:其實最後被調用的就是頂端裝飾返回的那個函數,你必須手動在這個函數中調用前面的函數,見這裏:
return '%s(%s)'%(n,f())
,纔會產生調用連的效果。因此裝飾器並不會幫你完成全部的事情,他給了你充分的自由。說的這裏,大家要問了(若是你有足夠的好奇心):‘裝飾器能帶參數嗎’。答案是能,見下面的代碼:
def wraper(n,f): def foo1(): return '%s(%s)'%(n,f()) return foo1 def deco(n): def deco1(f): print n,f return wraper(n,f) return deco1 @deco('decorate 1') @deco('decorate 2') def foo(): return 'hello' print foo()
這段代碼的執行結果爲:
decorate 2 <function foo at 0x0000000002117AC8> decorate 1 <function foo1 at 0x0000000002117B38> decorate 1(decorate 2(hello))
哇,和以前的結果如出一轍,代碼還被簡化了很多。這是怎麼回事呢,我來簡單解釋下,函數‘deco’不是裝飾器,他只是一個返回裝飾器的函數,當你把它放到裝飾符號‘@‘後面時,python的語法起了一個美妙的做用,他會先調用這個函數,而後用返回值值做爲裝飾器。
大概就是這樣啦,語法大師也許會描述的更詳細更專業。源碼狂人還可能深刻到python的C代碼裏尋找成因。不過,做爲一個有理智的好青年咱們就點到爲止吧。
下次,咱們再來掰扯一下,裝飾器均可以變出哪些戲法吧。洗洗睡了。