1. 程式人生 > >理解python中的裝飾器

理解python中的裝飾器

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節;




理解python中的裝飾器