天天看點

Python深入05 裝飾器

作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!

裝飾器最早在Python 2.5中出現,它最初被用于加工函數和方法這樣的可調用對象(callable object,這樣的對象定義有__call__方法)。在Python 2.6以及之後的Python版本中,裝飾器被進一步用于加工類。

我們先定義兩個簡單的數學函數,一個用來計算平方和,一個用來計算平方差:

在擁有了基本的數學功能之後,我們可能想為函數增加其它的功能,比如列印輸入。我們可以改寫函數來實作這一點:

我們修改了函數的定義,為函數增加了功能。

現在,我們使用裝飾器來實作上述修改:

裝飾器可以用def的形式定義,如上面代碼中的decorator。裝飾器接收一個可調用對象作為輸入參數,并傳回一個新的可調用對象。裝飾器建立了一個可調用對象,也就是上面的new_F。new_F中,我們增加了列印的功能,并通過調用F(a, b)來實作原有函數的功能。

定義好裝飾器後,我們就可以通過@文法使用了。在函數square_sum和square_diff定義之前調用@decorator,我們實際上将square_sum或square_diff傳遞給decorator,并将decorator傳回的新的可調用對象賦給原來的函數名(square_sum或square_diff)。 是以,當我們調用square_sum(3, 4)的時候,就相當于:

我們知道,Python中的變量名和對象是分離的。變量名可以指向任意一個對象。從本質上,裝飾器起到的就是這樣一個重新指向變量名的作用(name binding),讓同一個變量名指向一個新傳回的可調用對象,進而達到修改可調用對象的目的。

與加工函數類似,我們可以使用裝飾器加工類的方法。

如果我們有其他的類似函數,我們可以繼續調用decorator來修飾函數,而不用重複修改函數或者增加新的封裝。這樣,我們就提高了程式的可重複利用性,并增加了程式的可讀性。

在上面的裝飾器調用中,比如@decorator,該裝飾器預設它後面的函數是唯一的參數。裝飾器的文法允許我們調用decorator時,提供其它參數,比如@decorator(a)。這樣,就為裝飾器的編寫和使用提供了更大的靈活性。

在上面的例子中,裝飾器接收一個函數,并傳回一個函數,進而起到加工函數的效果。在Python 2.6以後,裝飾器被拓展到類。一個裝飾器可以接收一個類,并傳回一個類,進而起到加工類的效果。

在decorator中,我們傳回了一個新類newClass。在新類中,我們記錄了原來類生成的對象(self.wrapped),并附加了新的屬性total_display,用于記錄調用display的次數。我們也同時更改了display方法。

通過修改,我們的Bird類可以顯示調用display的次數了。

裝飾器的核心作用是name binding。這種文法是Python多程式設計範式的又一個展現。大部分Python使用者都不怎麼需要定義裝飾器,但有可能會使用裝飾器。鑒于裝飾器在Python項目中的廣泛使用,了解這一文法是非常有益的。