Python裝飾器(Decorator)簡介

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代碼裏尋找成因。不過,做爲一個有理智的好青年咱們就點到爲止吧。

下次,咱們再來掰扯一下,裝飾器均可以變出哪些戲法吧。洗洗睡了。

相關文章
相關標籤/搜索