原文出處: 田小計劃 html
裝飾模式有不少經典的使用場景,例如插入日誌、性能測試、事務處理等等,有了裝飾器,就能夠提取大量函數中與自己功能無關的相似代碼,從而達到代碼重用的目的。下面就一步步看看Python中的裝飾器。ide
一個簡單的需求函數
如今有一個簡單的函數」myfunc」,想經過代碼獲得這個函數的大概執行時間。性能
咱們能夠直接把計時邏輯方法」myfunc」內部,可是這樣的話,若是要給另外一個函數計時,就須要重複計時的邏輯。因此比較好的作法是把計時邏輯放到另外一個函數中(」deco」),以下:測試
可是,上面的作法也有一個問題,就是全部的」myfunc」調用處都要改成」deco(myfunc)」。spa
下面,作一些改動,來避免計時功能對」myfunc」函數調用代碼的影響:3d
通過了上面的改動後,一個比較完整的裝飾器(deco)就實現了,裝飾器沒有影響原來的函數,以及函數調用的代碼。例子中值得注意的地方是,Python中一切都是對象,函數也是,因此代碼中改變了」myfunc」對應的函數對象。指針
裝飾器語法糖日誌
在Python中,可使用」@」語法糖來精簡裝飾器的代碼:orm
使用了」@」語法糖後,咱們就不須要額外代碼來給」myfunc」從新賦值了,其實」@deco」的本質就是」myfunc = deco(myfunc)」,當認清了這一點後,後面看帶參數的裝飾器就簡單了。
被裝飾的函數帶參數
前面的例子中,被裝飾函數的自己是沒有參數的,下面看一個被裝飾函數有參數的例子:
從例子中能夠看到,對於被裝飾函數須要支持參數的狀況,咱們只要使裝飾器的內嵌函數支持一樣的簽名便可。
也就是說這時,」addFunc(3, 8) = deco(addFunc(3, 8))」。
這裏還有一個問題,若是多個函數擁有不一樣的參數形式,怎麼共用一樣的裝飾器?在Python中,函數能夠支持(*args, **kwargs)可變參數,因此裝飾器能夠經過可變參數形式來實現內嵌函數的簽名。
帶參數的裝飾器
裝飾器自己也能夠支持參數,例如說能夠經過裝飾器的參數來禁止計時功能:
經過例子能夠看到,若是裝飾器自己須要支持參數,那麼裝飾器就須要多一層的內嵌函數。
這時候,」addFunc(3, 8) = deco(True)( addFunc(3, 8))」,」myFunc() = deco(False)( myFunc ())」。
裝飾器調用順序
裝飾器是能夠疊加使用的,那麼這是就涉及到裝飾器調用順序了。對於Python中的」@」語法糖,裝飾器的調用順序與使用 @ 語法糖聲明的順序相反。
在這個例子中,」addFunc(3, 8) = deco_1(deco_2(addFunc(3, 8)))」。
Python內置裝飾器
在Python中有三個內置的裝飾器,都是跟class相關的:staticmethod、classmethod 和property。
staticmethod 是類靜態方法,其跟成員方法的區別是沒有 self 參數,而且能夠在類不進行實例化的狀況下調用
classmethod 與成員方法的區別在於所接收的第一個參數不是 self (類實例的指針),而是cls(當前類的具體類型)
property 是屬性的意思,表示能夠經過經過類實例直接訪問的信息
對於staticmethod和classmethod這裏就不介紹了,經過一個例子看看property。
注意,對於Python新式類(new-style class),若是將上面的 「@var.setter」 裝飾器所裝飾的成員函數去掉,則Foo.var 屬性爲只讀屬性,使用 「foo.var = ‘var 2′」 進行賦值時會拋出異常。可是,對於Python classic class,所聲明的屬性不是 read-only的,因此即便去掉」@var.setter」裝飾器也不會報錯。
總結
本文介紹了Python裝飾器的一些使用,裝飾器的代碼仍是比較容易理解的。只要經過一些例子進行實際操做一下,就很容易理解了。