理解python中的裝飾器

一 什麼是裝飾器?

        正如其名,裝飾器的作用是爲已經存在的對象增加額外功能(裝飾),由此可使已有函數在無需代碼改動的情況下增加額外功能;裝飾器的本質是嵌套的函數且返回函數對象,即閉包。有關閉包的概念,可參考《理解Python中的閉包》一文。


二 裝飾器應用場景舉例

        在介紹裝飾器之前,我們思考下遇到如下場景時的解決思路,然後在此基礎上,描述裝飾器的意義和旨在解決哪些問題。

        假如我們的func函數已經在使用,而且工作的挺好,func函數如下:

def func():
    print("執行func代碼塊")

  然而,某天針對該函數有新需求提出,暫時命名爲需求1:爲func函數增加性能度量,即測量函數的執行耗時。

  針對需求1,一種可以簡單理解的實現形式如下:

#實現1 增加執行耗時統計
def func():
    start_time = time.time()
    print("執行func代碼塊")
    end_time = time.time()
    print("執行耗時:%s"%(end_time-start_time))

   從實現來看,滿足了需求1,但如果其餘的函數也提出了類似的需求,如func1func2都需要增加耗時測量,按上面的實現方式,func1func2也需要在原功能前後增加time.time()代碼,這樣就造成了代碼重複和冗餘。

       針對代碼重複的問題,另一種實現方式是,定義一個專用的耗時測量函數,當需要測量某個函數時,直接將被測函數作爲該測量函數的入參,實現形式如下:

# 實現2將公共代碼抽離,定義專用函數
def count_time(func_name):
    start_time = time.time()
    func_name()
    end_time = time.time()
    print("執行耗時:%s"%(end_time-start_time))

   從上面實現來看,功能耗時統計的代碼被抽離並定義在共用函數count_time裏,當需要測量某個函數時,直接調用該count_time函數即可。

如:count_time(func)  #對func函數進行耗時統計
如:count_time(func1) #對func1函數進行耗時統計

   但這種實現也是有問題的,需求是希望直接調用func()函數即可完成對功能耗時的測量,但目前的實現方式,需要使用另外一個函數count_time,顯然改變了函數的調用方式。

 

裝飾器爲原有函數增加額外功能

針對上面場景描述及問題分析,我們使用裝飾器解決該問題,首先定義裝飾器函數如下:

#定義裝飾器函數
def timer(func_name):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func_name()
        end_time = time.time()
        print("執行耗時:%s" % (end_time - start_time))
    return wrapper


        從裝飾器的定義來看,其實就是一個閉包實現,滿足了在上篇對閉包三個條件的定義:

        1)  timer函數內部定義了wrapper函數,滿足函數嵌套;

        2)  內部函數wrapper使用了外部函數 timer的變量func_name

        3)  返回是一個內部函數的引用;

        因此,裝飾器本質是函數,是閉包的應用。

 

   現在,我們用裝飾器timer實現對func函數所提出的需求,即增加函數耗時測量功能。

   使用裝飾器的方法如下:

func = timer(func)#1
func() #2

  使用上面2步,調用func()時,雖然函數名相同,原func()函數也未做改動,但輸出結果已經增加了耗時統計功能。如果不理解,請繼續看上篇《理解Python中的閉包》中關於閉包的講解J

 

   另外,python的裝飾器有個更簡潔的表示方式,即使用“語法糖”@,如下:

@timer  #@語法糖 相當於 test1 = timer(test1) 只能放在定義函數的上面
def func():
   print("執行func代碼塊")

      #使用裝飾器語法糖@的調用方式

func()#直接調用即可


裝飾器的作用

   從上文分析,可以瞭解到,裝飾器主要用於爲已存在的函數對象附件額外的功能,而原先的函數的內部實現可以不做改動,調用方式也保持不變,而這些附加的功能是可以抽離出來作爲共用的,避免了相似場景下代碼的冗餘。

 

其他資源分享:

關於裝飾器:請參看http://i.youku.com/weiworld521 21節;




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章