做爲一個會寫函數的python開發,咱們從今天開始要去公司上班了。寫了一個函數,就交給其餘開發用了。python
def func1(): print('in func1')
季度末,公司的領導要給你們發績效獎金了,就提議對這段日子全部人開發的成果進行審覈,審覈的標準是什麼呢?就是統計每一個函數的執行時間。閉包
這個時候你要怎麼作呀?app
你一想,這好辦,把函數一改:ide
import time def func1(): start = time.time() print('in func1') print(time.time() - start) func1()
來公司半年,寫了2000+函數,挨個改一遍,1個禮拜過去了,等領導審覈完,再挨個給刪了。。。又1個禮拜過去了。。。這是否是很鬧心?函數
你以爲不行,不能讓本身費勁兒,告訴全部開發,如今大家都在本身本來的代碼上加上一句計算時間的語句?spa
import time def func1(): print('in func1') start = time.time() func1() print(time.time() - start)
仍是不行,由於這樣對於開發同事來說實在是太麻煩了。設計
那怎麼辦呢?你靈機一動,寫了一個timer函數。。。3d
import time def timer(func): start = time.time() func() print(time.time() - start) def func1(): print('in func1') def func2(): print('in func2') timer(func1) timer(func2)
這樣看起來是否是簡單多啦?無論咱們寫了多少個函數均可以調用這個計時函數來計算函數的執行時間了。。。儘管如今修改爲本已經變得很小很小了,可是對於同事來講仍是改變了這個函數的調用方式,假如某同事由於相信你,在他的代碼裏用你的方法用了2w屢次,那他修改完代碼大家友誼的小船也就完全地翻了。code
你要作的就是,讓你的同事依然調用func1,可是能實現調用timer方法的效果。blog
import time def timer(func): start = time.time() func() print(time.time() - start) def func1(): print('in func1') func1 =timer #要是能這樣的就完美了。。。惋惜報錯 func1()
很是惋惜,上面這段代碼是會報錯的,由於timer方法須要傳遞一個func參數,咱們不能在賦值的時候傳參,由於只要執行func1 = timer(func1),timer方法就直接執行了,下面的那句func1根本就沒有意義。到這裏,咱們的思路好像陷入了僵局。。。
裝飾器——簡單版
import time def func1(): print('in func1') def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer(func1) func1()
忙活了這麼半天,終於初具規模了!如今已經基本上完美了,惟一礙眼的那句話就是還要在作一次賦值調用。。。
你以爲礙眼,python的開發者也以爲礙眼,因此就爲咱們提供了一句語法糖來解決這個問題!
裝飾器——語法糖
import time def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner @timer #==> func1 = timer(func1) def func1(): print('in func1') func1()
到這裏,咱們能夠簡單的總結一下:
裝飾器的本質:一個閉包函數
裝飾器的功能:在不修改原函數及其調用方式的狀況下對原函數功能進行擴展
還有最後一個問題要解決,剛剛咱們討論的裝飾器都是裝飾不帶參數的函數,如今要裝飾一個帶參數的函數怎麼辦呢?
裝飾器——帶參數的裝飾器
def timer(func): def inner(a): start = time.time() func(a) print(time.time() - start) return inner @timer def func1(a): print(a) func1(1)
其實裝飾帶參的函數並非什麼難事,但假如你有兩個函數,須要傳遞的參數不同呢?
裝飾器——萬能傳參
import time def timer(func): def inner(*args,**kwargs): start = time.time() re = func(*args,**kwargs) print(time.time() - start) return re return inner @timer #==> func1 = timer(func1) def func1(a,b): print('in func1') @timer #==> func2 = timer(func2) def func2(a): print('in func2 and get a:%s'%(a)) return 'fun2 over' func1('aaaaaa','bbbbbb') print(func2('aaaaaa'))
如今參數的問題已經完美的解決了,但是若是你的函數是有返回值的呢?
裝飾器——帶返回值的裝飾器
import time def timer(func): def inner(*args,**kwargs): start = time.time() re = func(*args,**kwargs) print(time.time() - start) return re return inner @timer #==> func2 = timer(func2) def func2(a): print('in func2 and get a:%s'%(a)) return 'fun2 over' func2('aaaaaa') print(func2('aaaaaa'))
剛剛那個裝飾器已經很是完美了,可是正常咱們狀況下查看函數的一些信息的方法在此處都會失效
查看函數信息的方法
def index(): '''這是一個主頁信息''' print('from index') print(index.__doc__) #查看函數註釋的方法 print(index.__name__) #查看函數名的方法
爲了避免讓他們失效,咱們還要在裝飾器上加上一點來完善它:
裝飾器——warps demo
from functools import wraps def deco(func): @wraps(func) #加在最內層函數正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): '''哈哈哈哈''' print('from index') print(index.__doc__) print(index.__name__)
1.對擴展是開放的
爲何要對擴展開放呢?
咱們說,任何一個程序,不可能在設計之初就已經想好了全部的功能而且將來不作任何更新和修改。因此咱們必須容許代碼擴展、添加新功能。
2.對修改是封閉的
爲何要對修改封閉呢?
就像咱們剛剛提到的,由於咱們寫的一個函數,頗有可能已經交付給其餘人使用了,若是這個時候咱們對其進行了修改,頗有可能影響其餘已經在使用該函數的用戶。
裝飾器完美的遵循了這個開放封閉原則。
在不改變函數調用方式的基礎上在函數的前、後添加功能。
def timer(func): def inner(*args,**kwargs): '''執行函數以前要作的''' re = func(*args,**kwargs) '''執行函數以後要作的''' return re return inner
# wraps
from functools import wraps def deco(func): @wraps(func) #加在最內層函數正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper
帶參數的裝飾器
假如你有成千上萬個函數使用了一個裝飾器,如今你想把這些裝飾器都取消掉,你要怎麼作?
一個一個的取消掉? 沒日沒夜忙活3天。。。
過兩天你領導想通了,再讓你加上。。。
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''執行函數以前要作的''') re = func(*args,**kwargs) if flag: print('''執行函數以後要作的''') return re return inner return timer @outer(False) def func(): print(111) func()
有些時候,咱們也會用到多個裝飾器裝飾同一個函數的狀況。
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()