1. 程式人生 > >Python裝飾器的前世今生!

Python裝飾器的前世今生!

sig 方法 image name 高程 學習交流 高內聚 @class 結果

技術分享圖片

技術分享圖片

這樣做邏輯上是沒問題的,功能是實現了,但是我們調用的時候不再是調用真正的業務邏輯today函數,而是換成了logging_tool函數,這就破壞了原有的代碼結構,為了支持日誌功能,原有代碼需要大幅修改,那麽有沒有更好的方式的呢?當然有,答案就是裝飾器。

二、開天辟地

一個簡單的裝飾器

技術分享圖片

以上也是裝飾器的原理!!!

三、Pythonic世界的初探

@語法糖 接觸 Python 有一段時間的話,對 @ 符號一定不陌生了,沒錯 @ 符號就是裝飾器的語法糖,它放在函數開始定義的地方,這樣就可以省略最後一步再次賦值的操作

技術分享圖片

有了 @ ,我們就可以省去today = logging_tool(today)這一句了,直接調用 today() 即可得到想要的結果。 不需要對today() 函數做任何修改,只需在定義的地方加上裝飾器 ,調用的時候還是和以前一樣。 如果我們有其他的類似函數,可以繼續調用裝飾器來修飾函數 ,而不用重復修改函數或者增加新的封裝。這樣,提高程序可重復利用性,並增加程序的可讀性。

裝飾器在 Python 使用之所以如此方便,歸因於 Python函數能像普通的對象一樣能作為參數傳遞給其他函數,可以被賦值給其他變量,可以作為返回值,可以被定義在另外一個函數內。

技術分享圖片

技術分享圖片

2、讓裝飾器同時支持帶參數或不帶參數

技術分享圖片

如上所示,參數有兩種類型,一種是字符串,另一種是可調用的函數類型。因此,通過對參數類型的判斷即可實現支持帶參數和不帶參數的兩種情況。

3、類裝飾器

裝飾器不僅可以是函數,還可以是類,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器主要依靠類的__call__方法,當使用 @ 形式將裝飾器附加到函數上時,就會調用此方法。

(1)示例一、被裝飾函數不帶參數

技術分享圖片

(3)示例三、不依賴初始化函數,單獨使用__call__函數實現(體現類裝飾器靈活性大、高內聚、封裝性高的特點) 實現當一些重要函數執行時,打印日誌到一個文件中,同時發送一個通知給用戶

技術分享圖片

進一步擴展,給LogTool創建子類,來添加email的功能:

技術分享圖片

4、裝飾函數 -> 裝飾類

(1)函數層面的裝飾器很常見,以一個函數作為輸入,返回一個新的函數; (2)類層面的裝飾其實也類似,已一個類作為輸入,返回一個新的類;

例如:給一個已有的類添加長度屬性和getter、setter方法

技術分享圖片

五、上古神器

1、@property -> getter/setter方法

示例:給一個Student添加score屬性的getter、setter方法

技術分享圖片

2、@classmethod、@staticmethod

(1)@classmethod 類方法:定義備選構造器,第一個參數是類本身(參數名不限制,一般用cls) (2)@staticmethod 靜態方法:跟類關系緊密的函數

簡單原理示例:

技術分享圖片

3、@functools.wraps

裝飾器極大地復用了代碼,但它有一個缺點:因為返回的是嵌套的函數對象wrapper,不是原函數,導致原函數的元信息丟失,比如函數的docstring、 name 、參數列表等信息。不過呢,辦法總比困難多,我們可以通過@functools.wraps將原函數的元信息拷貝到裝飾器裏面的func函數中,使得裝飾器裏面的func和原函數有一樣的元信息。

技術分享圖片

@functools.wraps讓我們可以通過屬性__wrapped__直接訪問被裝飾的函數,同時讓被裝飾函數正確暴露底層的參數簽名信息

countdown.__wrapped__(1000) # 訪問被裝飾的函數print(inspect.signature(countdown)) # 輸出被裝飾函數的簽名信息

4、Easter egg

(1) 定義一個接受參數的包裝器

@decorator(x, y, z)def func(a, b):pass

等價於

func = decorator(x, y, z)(func)

即:decorator(x, y, z)的返回結果必須是一個可調用的對象,它接受一個函數作為參數並包裝它。

(2)一個函數可以同時定義多個裝飾器,比如:

技術分享圖片

歡迎關註我的博客或者公眾號:https://home.cnblogs.com/u/Python1234/ Python學習交流

Python裝飾器的前世今生!