[TOC] 裝飾器進階 通過上一篇已經知道, 如果還有概念還不理解的請返回裝飾器入門 "裝飾器入門" 裝飾器 實際也是一種函數, 他是 python語言的重要組成部分, 在實際開發中應用也相當廣泛. 正如我們所知, 前面所看到的被裝飾的函數都是最簡單函數格式, 這裡所說的簡單, 是指 ,`或者函數沒 ...
目錄
裝飾器進階
通過上一篇已經知道, 如果還有概念還不理解的請返回裝飾器入門裝飾器入門裝飾器 decorator
實際也是一種函數, 他是 python語言的重要組成部分, 在實際開發中應用也相當廣泛. 正如我們所知, 前面所看到的被裝飾的函數都是最簡單函數格式, 這裡所說的簡單, 是指 函數無參數的形式
,或者函數沒有返回(
直接執行結果)值在實際開發中我們遇到更多的其實是
擁有多個參數`,那麼這種裝飾器該如何編寫?
被裝飾的函數有參數
實例代碼
from functools import wraps def auth2(func): @wraps(func) def wrapper(*arg, **kwargs): user = input("pls input password:").strip() if user == "faily": print("---welcome login----") func(*arg, **kwargs) else: print("----wrong password----") return wrapper @auth2 def task2(name, is_happy="Yes"): print("{0} is doing somthing, Is he happy? [{1}]".format(name, is_happy)) if __name__ =="__main__": '''帶參數的裝飾器''' task2("faily")
可以看出,被裝飾的函數
task2
擁有兩個參數,name
,is_happy
, 那麼如何將兩個參數傳遞進裝飾器函數中呢?
- 根據前面我們所學的知識,可以看出, 裝飾器在執行的時候, 首先將被裝飾的
函數名
這裡是task2
傳遞給裝飾器函數auth2
, 那麼被裝飾函數的參數 就不得不找別的地方傳進去, 唯一可以接受這些變數的地方就是wrapper
函數. - 通過wrapper 函數將參數傳進去之後呢, 再建參數傳遞個 被裝飾的函數 即可
被裝飾的函數有返回值
顯然 這種函數最普遍, 函數在執行後立即展示結果,而是作為值進行傳遞
from functools import wraps def auth3(func): @wraps(func) def wrapper(arg): user = input("pls input password:").strip() if user == "faily": print("---welcome login----") return func(arg) else: print("----wrong password---") return wrapper @auth3 def task3(name): print("do somthing ") return name if __name__ == "__main__": ts = task3("momo") print(ts)
這種很好理解, 函數有返回值, 那麼在
wrapper
函數內執行完新加功能後, 直接return 被裝飾的函數即可
在函數中嵌入裝飾器
在這裡我們可以這樣理解: 既然裝飾器是高階函數 + 嵌套函數 = 裝飾器
那麼可不可 有這種組合呢,即 裝飾器 + 嵌套函數
的形式呢?
答案是肯定的
其實這種應用也可理解為. 之前我們寫的所有裝飾器
都是自己不帶參數的, 有沒有想過 帶有參數的 裝飾器該如何實現呢?
回到日誌的例子,並創建一個包裹函數,能讓我們指定一個用於輸出的日誌文件
from functools import wraps def logit(logfile='out.log'): # 裝飾器 def logging_decorator(func): @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " was called" print(log_string) # 打開logfile,並寫入內容 with open(logfile, 'a') as f: # 現在將日誌打到指定的logfile f.write(log_string + '\n') return func(*args, **kwargs) return wrapped_function return logging_decorator #預設裝飾器參數 @logit() def myfunc1(action="run"): if action == "run": return True else: return False #裝飾器傳參 @logit(logfile='func2.log') def myfunc2(action = "stop"): if action == "run": return True else: return False if __name__ == "__main__": myfunc1() myfunc2() # 現在一個叫做 func2.log 的文件出現了,裡面的內容就是上面的字元串
這個例子中, 是不是決定裝飾器實在是太強大了, 我們可以通過為裝飾器給予不同的參數實現不同函數日誌輸出的新增功能, 我們只是在
標準
裝飾器外外加了一層函數,這個函數實際就是為了用來將裝飾器的參數傳遞進包裹函數中.
裝飾器類
場景分析 : 在運維監控中, 我們常常需要記錄不同級別,或者不同app產生的日誌,但當我們的應用程式的某些部分還比較脆弱時,觸發異常
也許是需要更加關註的事情. 比方說有時只想記錄日誌到一個文件
; 而有時你想在程式發生異常時送到一個email,同時也保留日誌
。這是一個使用繼承的場景,但目前為止我們只看到過用來構建裝飾器的函數。
幸運的是,類也可以用來構建裝飾器。那我們現在以一個類而不是一個函數的方式,來重新構建logit
裝飾器類
rom functools import wraps class logit(object): '''裝飾器類''' def __init__(self, logfile='out.log'): self.logfile = logfile def __call__(self, func): # __call__說明這是一個callable @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " was called" print(log_string) # 打開logfile並寫入 with open(self.logfile, 'a') as f: # 現在將日誌打到指定的文件 f.write(log_string + '\n') # 現在,發送一個通知 self.notify() return func(*args, **kwargs) return wrapped_function def notify(self): # logit只打日誌,不做別的 pass
這個實現有一個附加優勢,在於比嵌套函數的方式更加整潔,而且包裹一個函數還是使用跟以前一樣的語法:
@logit() def myfunc1(): pass
現在,我們給
logit
創建子類,來添加email的功能。class email_logit(logit): ''' 一個logit的實現版本,可以在函數調用時發送email給管理員 ''' def __init__(self, email='[email protected]', *args, **kwargs): self.email = email super(email_logit, self).__init__(*args, **kwargs) # 集成父類 def notify(self): # 發送一封email到self.email # 這裡就不做實現了 pass
從現在起,
@email_logit
將會和@logit
產生同樣的效果,但是在輸出日誌的基礎上,還會多發送一封郵件給管理員。
總結
裝飾器進階已經講完, 下一篇我們繼續研究, 多重裝飾器