裝飾器函數(shù)
楔子
作為一個會寫函數(shù)的python開發(fā),我們從今天開始要去公司上班了。寫了一個函數(shù),就交給其他開發(fā)用了。
def func1():
? ? print('in func1')
季度末,公司的領導要給大家發(fā)績效獎金了,就提議對這段日子所有人開發(fā)的成果進行審核,審核的標準是什么呢?就是統(tǒng)計每個函數(shù)的執(zhí)行時間。
這個時候你要怎么做呀?
你一想,這好辦,把函數(shù)一改:
import timedef func1():
? ? start = time.time()
? ? print('in func1')
? ? print(time.time() - start)
func1()
來公司半年,寫了2000+函數(shù),挨個改一遍,1個禮拜過去了,等領導審核完,再挨個給刪了。。。又1個禮拜過去了。。。這是不是很鬧心?
你覺得不行,不能讓自己費勁兒,告訴所有開發(fā),現(xiàn)在你們都在自己原本的代碼上加上一句計算時間的語句?
import timedef func1():
? ? print('in func1')
start = time.time()
func1()print(time.time() - start)
還是不行,因為這樣對于開發(fā)同事來講實在是太麻煩了。
那怎么辦呢?你靈機一動,寫了一個timer函數(shù)。。。
import timedef timer(func):
? ? start = time.time()
? ? func()
? ? print(time.time() - start)def func1():
? ? print('in func1')def func2():
? ? print('in func2')
timer(func1)
timer(func2)
這樣看起來是不是簡單多啦?不管我們寫了多少個函數(shù)都可以調(diào)用這個計時函數(shù)來計算函數(shù)的執(zhí)行時間了。。。盡管現(xiàn)在修改成本已經(jīng)變得很小很小了,但是對于同事來說還是改變了這個函數(shù)的調(diào)用方式,假如某同事因為相信你,在他的代碼里用你的方法用了2w多次,那他修改完代碼你們友誼的小船也就徹底地翻了。
你要做的就是,讓你的同事依然調(diào)用func1,但是能實現(xiàn)調(diào)用timer方法的效果。
import timedef timer(func):
? ? start = time.time()
? ? func()
? ? print(time.time() - start)def func1():
? ? print('in func1')
func1 =timer? #要是能這樣的就完美了。。??上箦efunc1()
非??上?,上面這段代碼是會報錯的,因為timer方法需要傳遞一個func參數(shù),我們不能在賦值的時候傳參,因為只要執(zhí)行func1 = timer(func1),timer方法就直接執(zhí)行了,下面的那句func1根本就沒有意義。到這里,我們的思路好像陷入了僵局。。。
裝飾器的形成過程
import timedef func1():
? ? print('in func1')def timer(func):
? ? def inner():
? ? ? ? start = time.time()
? ? ? ? func()
? ? ? ? print(time.time() - start)
? ? return inner
func1 = timer(func1)
func1()
忙活了這么半天,終于初具規(guī)模了!現(xiàn)在已經(jīng)基本上完美了,唯一礙眼的那句話就是還要在做一次賦值調(diào)用。。。
你覺得礙眼,python的開發(fā)者也覺得礙眼,所以就為我們提供了一句語法糖來解決這個問題!
import timedef timer(func):
? ? def inner():
? ? ? ? start = time.time()
? ? ? ? func()
? ? ? ? print(time.time() - start)
? ? return inner
@timer? #==> func1 = timer(func1)def func1():
? ? print('in func1')
func1()
到這里,我們可以簡單的總結一下:
裝飾器的本質(zhì):一個閉包函數(shù)
裝飾器的功能:在不修改原函數(shù)及其調(diào)用方式的情況下對原函數(shù)功能進行擴展
還有最后一個問題要解決,剛剛我們討論的裝飾器都是裝飾不帶參數(shù)的函數(shù),現(xiàn)在要裝飾一個帶參數(shù)的函數(shù)怎么辦呢?
def timer(func):
? ? def inner(a):
? ? ? ? start = time.time()
? ? ? ? func(a)
? ? ? ? print(time.time() - start)
? ? return inner
@timerdef func1(a):
? ? print(a)
func1(1)
現(xiàn)在參數(shù)的問題已經(jīng)完美的解決了,可是如果你的函數(shù)是有返回值的呢?
import timedef 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'))
其實裝飾帶參的函數(shù)并不是什么難事,但假如你有兩個函數(shù),需要傳遞的參數(shù)不一樣呢?
import timedef 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'))
剛剛那個裝飾器已經(jīng)非常完美了,但是正常我們情況下查看函數(shù)的一些信息的方法在此處都會失效
def index():
? ? '''這是一個主頁信息'''? ? print('from index')print(index.__doc__)? ? #查看函數(shù)注釋的方法print(index.__name__)? #查看函數(shù)名的方法
為了不讓他們失效,我們還要在裝飾器上加上一點來完善它:
from functools import wrapsdef deco(func):
? ? @wraps(func) #加在最內(nèi)層函數(shù)正上方? ? def wrapper(*args,**kwargs):
? ? ? ? return func(*args,**kwargs)
? ? return wrapper
@decodef index():
? ? '''哈哈哈哈'''? ? print('from index')print(index.__doc__)print(index.__name__)
開放封閉原則
1.對擴展是開放的
為什么要對擴展開放呢?
我們說,任何一個程序,不可能在設計之初就已經(jīng)想好了所有的功能并且未來不做任何更新和修改。所以我們必須允許代碼擴展、添加新功能。
2.對修改是封閉的
為什么要對修改封閉呢?
就像我們剛剛提到的,因為我們寫的一個函數(shù),很有可能已經(jīng)交付給其他人使用了,如果這個時候我們對其進行了修改,很有可能影響其他已經(jīng)在使用該函數(shù)的用戶。
裝飾器完美的遵循了這個開放封閉原則。
裝飾器的主要功能和裝飾器的固定結構
裝飾器的主要功能:
在不改變函數(shù)調(diào)用方式的基礎上在函數(shù)的前、后添加功能。
裝飾器的固定格式:
def timer(func):
? ? def inner(*args,**kwargs):
? ? ? ? '''執(zhí)行函數(shù)之前要做的'''? ? ? ? re = func(*args,**kwargs)
? ? ? ? '''執(zhí)行函數(shù)之后要做的'''? ? ? ? return re
? ? return inner
from functools import wrapsdef deco(func):
? ? @wraps(func) #加在最內(nèi)層函數(shù)正上方? ? def wrapper(*args,**kwargs):
? ? ? ? return func(*args,**kwargs)
? ? return wrapper
帶參數(shù)的裝飾器
假如你有成千上萬個函數(shù)使用了一個裝飾器,現(xiàn)在你想把這些裝飾器都取消掉,你要怎么做?
一個一個的取消掉? 沒日沒夜忙活3天。。。
過兩天你領導想通了,再讓你加上。。。
def outer(flag):
? ? def timer(func):
? ? ? ? def inner(*args,**kwargs):
? ? ? ? ? ? if flag:
? ? ? ? ? ? ? ? print('''執(zhí)行函數(shù)之前要做的''')
? ? ? ? ? ? re = func(*args,**kwargs)
? ? ? ? ? ? if flag:
? ? ? ? ? ? ? ? print('''執(zhí)行函數(shù)之后要做的''')
? ? ? ? ? ? return re
? ? ? ? return inner
? ? return timer
@outer(False)def func():
? ? print(111)
func()
多個裝飾器裝飾同一個函數(shù)
有些時候,我們也會用到多個裝飾器裝飾同一個函數(shù)的情況
def wrapper1(func):
? ? def inner():
? ? ? ? print('wrapper1 ,before func')
? ? ? ? func()
? ? ? ? print('wrapper1 ,after func')
? ? return innerdef wrapper2(func):
? ? def inner():
? ? ? ? print('wrapper2 ,before func')
? ? ? ? func()
? ? ? ? print('wrapper2 ,after func')
? ? return inner
@wrapper2
@wrapper1def f():
? ? print('in f')
f()