python裝飾器本質上就是一個函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外的功能,裝飾器的返回值也是一個函數對象。
不少python初學者學到面向對象類和方法是一道大坎,那麼python中的裝飾器是你進入Python高級語法大門的一道坎。python
假設你寫了幾個函數,有一天領導心血來潮說,你把每一個函數的運行時長(結束時間-開始時間)統計下,做爲一個python實習生的你可能會這樣寫web
import time def func_a(): print("hello") time.sleep(0.5) def func_b(): print("world") time.sleep(0.8) if __name__ == '__main__': func_a() func_b()
做爲一個實習生的你,可能想到的解決辦法以下app
import time def func_a(): start = time.time() print("hello") time.sleep(0.5) end = time.time() print("運行時長:%.4f 秒" % (end-start)) def func_b(): start = time.time() print("world") time.sleep(0.8) end = time.time() print("運行時長:%.4f 秒" % (end-start)) if __name__ == '__main__': func_a() func_b()
運行結果:框架
hello 運行時長:0.5009 秒 world 運行時長:0.8008 秒
上面的代碼雖然知足了領導的要求,可是若是你寫的函數不少的話,每一個函數都這樣去添加,會顯得代碼很臃腫,有不少重複代碼。
有一天你邊上的一個python老司機看了下你的代碼,給你指了條路:裝飾器函數
裝飾器能夠寫成函數式裝飾器,也能夠寫成一個類裝飾器,先從簡單的函數裝飾器開始學習。
python裝飾器本質上就是一個函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外的功能,裝飾器的返回值也是一個函數對象。學習
runtime函數就是一個裝飾器了,它對原函數作了包裝並返回了另一個函數,額外添加了一些功能。在函數上方使用@語法糖就能夠調用這個裝飾器了測試
import time def runtime(func): def wrapper(): start = time.time() f = func() # 原函數 end = time.time() print("運行時長:%.4f 秒" % (end-start)) return f return wrapper @runtime def func_a(): print("hello") time.sleep(0.5) @runtime def func_b(): print("world") time.sleep(0.8) if __name__ == '__main__': func_a() func_b()
運行結果ui
hello 運行時長:0.5001 秒 world 運行時長:0.8001 秒
上面的runtime就是一個簡單的裝飾器模型了,但並不強壯,若是函數裏面帶有參數,那就無論用了,而且函數的參數是不固定的,這時候就須要用到*args
,**kwargs
兩兄弟了日誌
import time def runtime(func): def wrapper(*args, **kwargs): start = time.time() f = func(*args, **kwargs) # 原函數 end = time.time() print("運行時長:%.4f 秒" % (end-start)) return f return wrapper @runtime def func_a(a): print("hello"+a) time.sleep(0.5) @runtime def func_b(b, c="xx"): print("world"+b+c) time.sleep(0.8) if __name__ == '__main__': func_a("a") func_b("b", c="xxx")
關於__call__
方法,不得不先提到一個概念,就是可調用對象(callable),咱們平時自定義的函數、內置函數和類都屬於可調用對象,
但凡是能夠把一對括號()應用到某個對象身上均可稱之爲可調用對象,判斷對象是否爲可調用對象能夠用函數 callable。
若是在類中實現了__call__
方法,那麼實例對象也將成爲一個可調用對象code
import time class runtime(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): start = time.time() f = self.func(*args, **kwargs) # 原函數 end = time.time() print("運行時長:%.4f 秒" % (end-start)) return f @runtime def func_a(a): print("hello"+a) time.sleep(0.5) @runtime def func_b(b, c="xx"): print("world"+b+c) time.sleep(0.8) if __name__ == '__main__': func_a("a") func_b("b", c="xxx")
快到年末了,領導說運行的速度先不要太快了,讓客戶先加錢,而後再以正常的速度顯示,那麼如今的需求是讓每一個函數的運行時間加50%,該如何實現呢?
這就到了裝飾器的高級語法,裝飾器也須要帶上參數了
import time def runtime(slowly=1): def wrapper(func): def inner_wrapper(*args, **kwargs): start = time.time() f = func(*args, **kwargs) # 原函數 end = time.time() t = end-start time.sleep((slowly-1)*t) # 延遲效果 new_end = time.time() print("運行時長:%.4f 秒" % (new_end-start)) return f return inner_wrapper return wrapper @runtime(1.5) def func_a(a): print("hello"+a) time.sleep(0.5) @runtime(1.5) def func_b(b, c="xx"): print("world"+b+c) time.sleep(0.8) if __name__ == '__main__': func_a("a") func_b("b", c="xxx")
import time class runtime(object): def __init__(self, slowly=1): self.slowly = slowly def __call__(self, func): def wrapper(*args, **kwargs): start = time.time() f = func(*args, **kwargs) # 原函數 end = time.time() t = end-start time.sleep((self.slowly-1)*t) # 延遲效果 new_end = time.time() print("運行時長:%.4f 秒" % (new_end-start)) return f return wrapper @runtime(1.5) def func_a(a): print("hello"+a) time.sleep(0.5) @runtime(1.5) def func_b(b, c="xx"): print("world"+b+c) time.sleep(0.8) if __name__ == '__main__': func_a("a") func_b("b", c="xxx")
用哪些地方須要使用裝飾器呢?
@task(1)
,@pytest.fixture(scope="function")
@allure.step('修改購物車')
@login_required
python自動化交流 QQ羣:779429633