[Python學習]decorator的使用

來自:limodou的學習記錄 python

[Python學習]decorator的使用 web

在我之前介紹 Python 2.4 特性的Blog中已經介紹過了decorator了,不過,那時是照貓畫虎,如今再仔細描述一下它的使用。 編程

關於decorator的詳細介紹在 Python 2.4中的What’s new中已經有介紹,你們能夠看一下。 函數

如何調用decorator

基本上調用decorator有兩種形式 學習

第一種:

@A
def f ():
    … spa

這種形式是decorator不帶參數的寫法。最終 Python 會處理爲: .net

f = A(f) 日誌

還能夠擴展成: orm

@A
@B
@C
def f ():
    … 對象

最終 Python 會處理爲:

f = A(B(C(f)))

注:文檔上寫的是@A @B @C的形式,但其實是不行的,要寫成多行。 並且執行順序是按函數調用順序來的,先最下面的C,而後是B,而後是A。所以,若是decorator有順序話,必定要注意:先要執行的放在最下面,最後執行的放在最上面。 (應該不存在這種倒序的關係)

第二種:

@A(args)
def f ():
    …

這種形式是decorator帶參數的寫法。那麼 Python 會處理爲:

def f(): …
_deco = A(args)
f = _deco(f)

能夠看出, Python 會先執行A(args)獲得一個decorator函數,而後再按與第一種同樣的方式進行處理。

decorator函數的定義

每個decorator都對應有相應的函數,它要對後面的函數進行處理,要麼返回原來的函數對象,要麼返回一個新的函數對象。請注意,decorator只用來處理函數和類方法。

第一種:

針對於第一種調用形式

def A(func):
    #處理func
    #如func.attr=’decorated’
    return func
@A
def f(args):pass

上面是對func處理後,仍返回原函數對象。這個decorator函數的參數爲要處理的函數。若是要返回一個新的函數,能夠爲:

def A(func):
    def new_func(args):
        #作一些額外的工做
        return func(args) #調用原函數繼續進行處理
    return new_func
@A
def f(args):pass

要注意 new_func的定義形式要與待處理的函數相同,所以還能夠寫得通用一些,如:

def A(func):
    def new_func(*args, **argkw):
        #作一些額外的工做
        return func(*args, **argkw) #調用原函數繼續進行處理
    return new_func
@A
def f(args):pass

能夠看出,在A中定義了新的函數,而後A返回這個新的函數。在新函數中,先處理一些事情,好比對參數進行檢查,或作一些其它的工做,而後再調原始的函數進行處理。這種模式能夠當作,在調用函數前,經過使用decorator技術,能夠在調用函數以前進行了一些處理。若是你想在調用函數以後進行一些處理,或者再進一步,在調用函數以後,根據函數的返回值進行一些處理能夠寫成這樣:

def A(func):
    def new_func(*args, **argkw):
        result = func(*args, **argkw) #調用原函數繼續進行處理
        if result:
            #作一些額外的工做
            return new_result
        else:
            return result
    return new_func
@A
def f(args):pass

第二種:

針對第二種調用形式

在文檔上說,若是你的decorator在調用時使用了參數,那麼你的decorator函數只會使用這些參數進行調用,所以你須要返回一個新的decorator函數,這樣就與第一種形式一致了。

def A(arg):
    def _A(func):
        def new_func(args):
            #作一些額外的工做
            return func(args)
        return new_func
    return _A
@A(arg)
def f(args):pass

能夠看出A(arg)返回了一個新的 decorator _A。

decorator的應用場景

不過我也一直在想,到底decorator的魔力是什麼?適合在哪些場合呢?是否我須要使用它呢?

decorator的魔力就是它能夠對所修飾的函數進行加工。那麼這種加工是在不改變原來函數代碼的狀況下進行的。有點象我知道那麼一點點的AOP(面向方面編程)的想法。

它適合的場合我能想到的列舉出下:

  1. 象文檔中所說,最初是爲了使調用staticmethod和classmethod這樣的方法更方便
  2. 在某些函數執行前作一些工做,如web開發中,許多函數在調用前須要先檢查一下用戶是否已經登陸,而後才能調用
  3. 在某此函數執行後作一些工做,如調用完畢後,根據返回狀態寫日誌
  4. 作參數檢查

可能還有許多,你能夠自由發揮想象

那麼我須要用它嗎?

我想那要看你了。不過,我想在某些狀況下,使用decorator能夠增長程序的靈活性,減小耦合度。好比前面所說的用戶登陸檢查。的確能夠寫一個通用的登陸檢查函數,而後在每一個函數中進行調用。但這樣會形成函數不夠靈活,並且增長了與其它函數之間的結合程度。若是用戶登陸檢查功能有所修改,好比返回值的判斷髮生了變化,有可能每一個用到它的函數都要修改。而使用decorator不會形成這一問題。同時使用decorator的語法也使得代碼簡單,清晰(一但你熟悉它的語法的話)。固然你不使用它是能夠的。不過,這種函數之間相互結合的方式,更符合搭積木的要求,它能夠把函數功能進一步分解,使得功能足夠簡單和單一。而後再經過decorator的機制靈活的把相關的函數串成一個串,這麼一想,還真是不錯。好比下面:

@A
@B
def account(args):pass

假設這是一個記賬處理函數,account只管記賬。但一個真正的記賬還有一些判斷和處理,好比:B檢查賬戶狀態,A記日誌。這樣的效果實際上是先檢查B、經過在A中的處理能夠先執行account,而後再進行記日誌的處理。象搭積木同樣很方便,改起來也容易。甚至能夠把account也寫成decorator,而下面執行的函數是一個空函數。而後再經過配置文件等方法,將decorator的組合保存起來,就基本實現功能的組裝化。是否是很是理想。

Python 帶給人的創造力真是無窮啊!

相關文章
相關標籤/搜索