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()
參數預設是yasoob,是以函數name
被傳回了。如果我們把語句改為greet
,那麼a = hi(name = "momo")
函數将被傳回。我們還可以列印出welcome
,這會輸出now you are in the greet() function。hi()()
- 其實并不需要在一個函數裡去執行另一個函數,也可以将其作為輸出傳回出來
- 高階函數
- 定義: 把一個函數名當作實參傳遞給另外一個函數
# 計算函數的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()
. 這并不是我們想要的!Ouput輸出應該是print(task.__name__) #output wrapTheFunction
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接受一個函數來進行裝飾,并加入了複制函數名稱、注釋文檔、參數清單等等的功能。這可以讓我們在裝飾器裡面通路在裝飾之前的函數的屬性。
- 作者: @failymao 本文為作者原創,轉載請注明出處: https://www.cnblogs.com/failymao/p/10454247.html
- v1 将函數作為參數傳遞進 嵌套函數中, 傳回函數本身