裝飾器
裝飾器是干什么用的?
裝飾器可以在不修改某個函數的情況下,給函數添加功能。
形象點來說,從前有一個王叔叔,他一個人住在家里,每天打掃家,看書。於是定義如下一個函數:
def uncle_wang():
sweeping()
reading()
后來呢,有一天,大頭兒子一家搬到了王叔叔隔壁 😄 。根據劇情,一天,大頭兒子的媽媽請王叔叔來家里吃飯,那么,王叔叔的日程就添加了“去隔壁吃飯”這一項,但是又不能修改王叔叔之前的日程,怎么實現?這時,就可以給王叔叔添加一個裝飾器,給這個裝飾器起個名字,姑且就叫neighbor
,然后就寫成這樣:
@neighbor
def uncle_wang():
sweeping()
reading()
然后王叔叔去大頭兒子家吃飯就提上日程啦,哈哈。(只是吃飯哦,不要想多了(⊙v⊙))
這個neighbor
其實也是個函數,參數就是uncle_wang
(沒錯,可以把函數名當成參數傳來傳去,還能當做返回值),在裝飾器里面實現“去隔壁吃飯”:
def neighbor(func):
eat_next_door() # 自定義函數,去隔壁吃飯
return func # 原來的函數不變,直接返回
最后是執行,直接運行uncle_wang()
即可
# 執行函數
uncle_wang() # 相當於不加裝飾器,直接執行 neighbor(uncle_wang)()
這感覺就像是用neighbor
裝飾了uncle_wang
,豐富了王叔叔的生活,從此變成了隔壁老王。實現方式就是套娃,給uncle_wang
套個neighbor
,變成neighbor(uncle_wang)
(這整個東西是個函數名),然后調用這個函數: neighbor(uncle_wang)()
,形如:函數名()
在Python里,這個套娃的操作簡化成了裝飾器,直接在原函數上面添加@neighbor
,然后調用的時候還是寫成uncle_wang()
,但是這個裝飾過的王叔叔已經不是原來的王叔叔了,他現在其實是隔壁老王。
王叔叔的新日程搞定了,但是還有個問題,就是順序。現在的日程順序相當於:
eat_next_door()
sweeping()
reading()
請人吃飯當然是吃晚飯啦,所以eat_next_door()
需要排在最后面,而neighbor
函數不能先返回(return func
)然后才執行eat_next_door()
,眾所周知,函數返回了就結束了,后面的東西都不管了。
所以,繼續套娃,再搞個函數進去,寫成這樣:
def neighbor(func):
def wrapper(): # 套娃函數,注意這里是定義,不是執行
func() # 相當於不帶裝飾器的 uncle_wang()
eat_next_door() # 自定義函數,去隔壁吃飯
return wrapper # 直接返回套娃函數
這樣順序就對了,王叔叔很滿意~
現在這個裝飾器基本成型了,但是現在還不能處理原函數的參數和裝飾器函數的參數,繼續改進的實現方式可以去看廖雪峰老師的教程,寫得很不錯,我就是從那學來的。
附上完整代碼:
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
def sweeping():
print('sweeping')
def reading():
print('reading')
def eat_next_door():
print('eat_next_door')
def neighbor(func):
def wrapper(): # 套娃函數,注意這里是定義,不是執行
func() # 相當於不帶裝飾器的 uncle_wang()
eat_next_door() # 自定義函數,去隔壁吃飯
return wrapper # 直接返回套娃函數
@neighbor
def uncle_wang():
sweeping()
reading()
if __name__ == "__main__":
uncle_wang()
(嗨,又水了一篇,之前還說要測一下手動實現和庫函數實現的二分查找的耗時差距,正事還是放到下次吧……)
參考資料: