[TOC] python裝飾器初級 認識裝飾器 概念: 簡單地說: 原則 : 不修改被裝飾函數的源代碼 不修改被裝飾函數的調用方式 優點: 有助於讓我們的代碼更簡短,也更Pythonic(Python範兒 應用場景: 在項目迭代過程中,需要不停的為某一個功能(函數)新增或刪除某些小功能, 如果可復用 ...
目錄
python裝飾器初級
認識裝飾器
- 概念:
- 簡單地說:
他們是為其他函數的新增功能的函數
- 簡單地說:
- 原則 :
- 不修改被裝飾函數的源代碼
- 不修改被裝飾函數的調用方式
- 優點:
- 有助於讓我們的代碼更簡短,也更Pythonic(Python範兒
- 應用場景:
- 在項目迭代過程中,需要不停的為某一個功能(函數)新增或刪除某些小功能, 如果可復用的函數本來比較多, 在如果不停的對原始函數進行修改, 這將會是一件煩躁和重覆的工作. 而使用裝飾器就可以很完美的解決這種問題, 如web用戶的的授權管理,日誌輸出等!
知識儲備
函數即變數
- 函數本質在電腦的記憶體中的一個單獨記憶體空間, 函數名如同其他類型(int, str, list...)變數名一樣,可以進行賦值,傳遞
嵌套函數 :
定義: 在一個函數中定義另一個函數:
def hi(name="faily"): print("now you are inside the hi() function") def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" print(greet()) print(welcome()) print("now you are back in the hi() function") if __name__ == "__main__": hi()
從函數中返回函數
其實並不需要在一個函數里去執行另一個函數,也可以將其作為輸出返回出來
def hi(name="faily"): def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" if name == "faily": return greet else: return welcome if __name__ == "__main__" a = hi() # 此時返回的a實際是greet函數名 print(a) # 執行greet函數
在
if/else
語句中我們返回greet
和welcome
,而不是greet()
和welcome()
。為什麼那樣?這是因為當你把一對小括弧放在後面,這個函數就會執行;然而如果你不放括弧在它後面,那它可以被到處傳遞,並且可以賦值給別的變數而不去執行它。當我們寫下
a = hi()
,hi()
會被執行,而由於name
參數預設是yasoob,所以函數greet
被返回了。如果我們把語句改為a = hi(name = "momo")
,那麼welcome
函數將被返回。我們還可以列印出hi()()
,這會輸出now you are in the greet() function。
高階函數
定義: 把一個函數名當作實參傳遞給另外一個函數
# 計算函數的bar運行時間 import time def bar(): time.sleep(3) print("in the bar") def run_time(func): t1 = time.time() func() t2 = time.time() print("the func:{0} run time is {1}".format(func.__name__,t2-t1)) if __name__ == "__main__": run_time(bar)
缺點: 雖然實現了此功能,但是改變了
bar
函數名
裝飾器演變
實際就是:
高階函數
+嵌套函數
的結合體認證 裝飾器演變
v1 將函數作為參數傳遞進 嵌套函數中, 返回函數本身
def cert_decorator(func): def wrapTheFunction(): user = input("pls input password:").strip() if user == "faily": print("---welcome login----") func() else: print("----wrong password") return wrapper def task(): print("do somthing ") if __name__ == "__main__": task = cert_decorator(task) # 執行裝飾器函數,將task作為參數傳遞,賦值給task變數,返回的是wrapper函數名,內部函數func並沒有執行 task() # 執行task,實際就是執行了wrapper(),然後執行了內部的func()函數
實現了不改變函數名的和內部結構的前提下, 實現了認證,但是, 但是... 用戶需要多些一行重命名函數變數的方式
v2 python內部可以通過
@
符號進行直接調用def cert_decorator(func): def wrapTheFunction(): user = input("pls input password:").strip() if user == "faily": print("---welcome login----") func() else: print("----wrong password") return wrapTheFunction @cert_decorator def task(): print("do somthing ") if __name__ == "__main__": task()
※ 好像我們大功告成了... 別高興得太早 ,如果我們運行一下這行代碼 會存在一個問題
print(task.__name__) #output wrapTheFunction
♥ . 這並不是我們想要的!Ouput輸出應該是
task
。這裡的函數被warpTheFunction替代了。它重寫了我們函數的名字和註釋文檔(docstring), 如果我們想在被裝飾的函數在執行之前去訪問函數的某些屬性(文檔, 變數名...),顯然是沒辦法做到的, 怎麼辦???幸運的是Python提供給我們一個簡單的函數來解決這個問題,那就是
functools.wraps
。我們修改上一個例子來使用functools.wraps
v3 使用functools.wraps
from functools import wraps def cert_decorator(func): @wraps(func) def wrapTheFunction(): user = input("pls input password:").strip() if user == "faily": print("---welcome login----") func() else: print("----wrong password----") return wrapTheFunction @cert_decorator def task(): print("do somthing ") if __name__ == "__main__": task() print(task.__name__) # output pls input password:mm ----wrong password---- task
註意:@wraps接受一個函數來進行裝飾,並加入了複製函數名稱、註釋文檔、參數列表等等的功能。這可以讓我們在裝飾器裡面訪問在裝飾之前的函數的屬性。