目錄
- 裝飾器進階
- 1. 被裝飾的函數有多個參數。
- 2. 被裝飾的函數有傳回值
- 3.在函數中嵌入裝飾器
- 4. 裝飾器類
- 總結
裝飾器進階
通過上一篇已經知道, 如果還有不了解什麼是裝飾器,請傳回 裝飾器入門。 前面一篇算是裝飾器的入門 。裝飾器在 python語言中有着非常重要的應用。小到腳本的開發,大到很多功能負責的web架構開發。正如我們所知, 前面所看到的被裝飾的
函數
都是最簡單函數格式, 這裡所說的簡單, 是指
函數無參數的形式
,或者
函數沒有傳回(直接執行結果)值
。 在實際開發中 被裝飾的函數并非我們見到如此簡單的格式,更多的複雜的函數如
多傳回值
,
多參數(形式參數,關鍵字參數
, 是以這一篇算是python裝飾器的一個進階。
1. 被裝飾的函數有多個參數。
場景一: 一個簡單的認證裝飾器
- 執行個體代碼
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 函數将參數傳進去之後呢, 再建參數傳遞個 被裝飾的函數 即可
2. 被裝飾的函數有傳回值
- 顯然 這種函數最普遍, 函數在執行後立即展示結果,而是作為值進行傳遞
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
3.在函數中嵌入裝飾器
在這裡我們可以這樣了解: 既然裝飾器是
高階函數 + 嵌套函數 = 裝飾器
那麼可不可以有這種組合呢, 即
嵌套函數 + 裝飾器函數
的形式呢?
答案是
肯定的
其實這種應用也可了解為: 之前我們寫的所有
裝飾器函數
自己都不帶參數的,既然
裝飾器本質也是函數
,那麼裝飾器函數是否可以帶參數呢?? 格式是什麼樣子呢?
通過下面的例子進行說明
- 回到日志的例子,并建立一個包裹函數,能讓我們指定一個用于輸出的日志檔案
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 + ' ') 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 的檔案出現了,裡面的内容就是上面的字元串
這個例子中, 是不是覺得裝飾器實在是太強大了?
- 可以通過為裝飾器給予不同的參數實作不同函數日志輸出的新增功能, 我們隻是在
外加了一層函數, 這個函數實際就是為了用來将裝飾器的參數傳遞進包裹函數中.标準裝飾器
4. 裝飾器類
場景分析 : 在運維監控中, 我們常常需要記錄不同級别,或者不同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 + ' ') # 現在,發送一個通知 self.notify() return func(*args, **kwargs) return wrapped_function def notify(self): # logit隻打日志,不做别的 pass
這個實作有一個附加優勢,在于比嵌套函數的方式更加整潔,而且包裹一個函數還是使用跟以前一樣的文法:
現在,我們給@logit() def myfunc1(): pass
建立子類,來添加email的功能。logit
從現在起,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
總結
裝飾器進階已經講完, 下一篇我們繼續研究, 多重裝飾器