這篇文章主要介紹了Python裝飾器函數怎麼使用的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Python裝飾器函數怎麼使用文章都會有所收獲,下面我們一起來看看吧。
假如我寫了一個函數 f
def f(): print('hello')
之後我想知道這段函數執行所要的時間,這好辦,我只要將代碼改為如下就行
import timedef f(): start = time.time() #獲取程序執行開始的時間 print('hello') end = time.time() #獲取程序執行結束的時間 print(end - start) #得出函數f執行所要時間f()
但之後我有寫了無數個函數f2,f3……fn,我想知道每個函數執行所需要的時間,那麼如果都像上面一樣改,豈不是很鬧心?還是不行,因為這樣實在是太麻煩了。那怎麼辦呢?於是靈機一動,寫了一個timer函數。。。
import timedef timer(func): start = time.time() func() print(time.time() - start)def f(): print('hello')def f2(): print('xorld')timer(f)timer(f2)
這樣看起來是不是簡單多啦?不管我們寫了多少個函數都可以調用這個計時函數來計算函數的執行時間
但是如果我只想用原來的方式f1(),f2(),fn()調用了這個函數,函數在原本執行輸出的結果不變的前提下還可以增加計算時間的功能,而不是調用timer(f),timer(f2)才能計算時間,這該怎麼辦呢?
看了下面的裝飾器函數你就會知道如何解決這個問題
以下就是解決上面問題的代碼的簡單版:
import timedef f(): print('hello')def timer(func): def inner(): start = time.time() func() print(time.time() - start) return innerf = timer(f)f()
還是這句話我只想用原來的方式f1(),f2(),fn()調用了這個函數,函數在原本執行輸出的結果不變的前提下還可以增加計算時間的功能,但我還是要在函數 f 執行前寫 f = timer(f)在這一串代碼,是不是覺得礙眼?python的開發者也覺得礙眼,所以python的開發者就為我們提供了一句語法糖來解決這個問題!
用@timmer代替f = timer(f),這就是一句語法糖。
import timedef timer(func): def inner(): start = time.time() func() print(time.time() - start) return [email protected] #==> 寫著這句話就相當於執行了f = timer(f)def f(): print('hello')f()
1、本質
裝飾器的本質就是一個閉包函數
2、功能
在不修改原函數及其調用方式的情況下對原函數功能進行擴展
剛才我們寫的裝飾器都是裝飾不帶參數的函數,現在要裝飾一個帶參數的函數怎麼辦呢?
import timedef timer(func): def inner(a): start = time.time() func(a) print(time.time() - start) return [email protected] f(a): print(a)f('hello')
其實裝飾帶參的函數並不是什麼難事,但假如你有兩個函數,需要傳遞的參數不一樣呢,比如 函數func1有兩個參數func1(a ,b),函數func 2只有一個參數func2(a), 且它們都想用這個裝飾器裝飾,做到計算函數執行時間?這怎麼辦呢?那就用下面代碼。
import timedef timer(func): def inner(*args,**kwargs): start = time.time() re = func(*args,**kwargs) print(time.time() - start) return re return [email protected] #==> 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'))輸出結果:in func10.0in func2 and get a:aaaaaa0.0fun2 over
現在參數的問題已經完美的解決了,可是如果你的函數是有返回值的呢?用上面的代碼你就拿不到返回值了那究竟要如何解決這個問題呢?那就看下面的代碼吧!
import timedef timer(func): def inner(*args,**kwargs): start = time.time() re = func(*args,**kwargs) print(time.time() - start) return re return [email protected] #==> func2 = timer(func2)def func2(a): print('in func2 and get a:%s'%(a)) return 'fun2 over'func2('aaaaaa')print(func2('aaaaaa'))輸出結果:in func2 and get a:aaaaaa0.0in func2 and get a:aaaaaa0.0fun2 over
有些時候,我們也會用到多個裝飾器裝飾同一個函數的情況。
ef wrapper1(func): #func ----- f def inner1(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner1def wrapper2(func): def inner2(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return [email protected] #f = wrapper2(f) ----->> wrapper2(inner1) == [email protected] #f = wrapper1(f) = innerdef f(): print('in f')f() #===>>inner2#多個裝飾器裝飾同一個函數輸出結果:wrapper2 ,before funcwrapper1 ,before funcin fwrapper1 ,after funcwrapper2 ,after func
上面那個裝飾器已經非常beautiful了,但是還有一個問題,如果我給代碼中無數個函數都加了@timer這個語法糖,如果之後我又不想用它了那豈不是又要每個去將它注釋,沒日沒夜忙活3天?豈不是特別麻煩,為了使裝飾器不用時能夠更好的回收而不是一個一個去注釋或者刪除 我們引入帶參數的裝飾器概念
'''為了使裝飾器不用時能夠更好的回收而不是一個一個去注釋或者刪除我們引入帶參數的裝飾器概念'''import time'''FLAGE的目的是用它控制裝飾器的開關,那麼當我們不用的時候就不要一個一個去注釋只需將True改為False就行'''FLAGE = Truedef timmer_out(flag): def timmer(func): def inner(*args,**kwargs): if flag: start = time.time() ret = func(*args,**kwargs) end = time.time() print(end - start) return ret else: ret = func(*args, **kwargs) return ret return inner return [email protected]_out(FLAGE)#timmer_out(FLAGE)# 也相當於執行 timmer_out(FLAGE)--->>返回timmer———————>>@timmer(wahaha = timmer(wahaha))def wahaha(): time.sleep(0.1) #不休息的話函數執行的太快難以計算時間 print('wahahahahahaha')wahaha()@timmer_out(FLAGE)def erguotou(): time.sleep(0.1) #不休息的話函數執行的太快難以計算時間 print('erguotoutoutou')erguotou()輸出結果:wahahahahahaha0.10152268409729004erguotoutoutou0.10795140266418457
'''print(wahaha.__name__) #查看字符串格式的函數名print(wahaha.__doc__) #查看一個函數的注釋'''#下面用__name__查看holiday的函數名from functools import wrapsdef wrapper(func): @wraps(func) #加在最內層函數正上方 def inner(*args,**kwargs): print('在被裝飾的函數執行之前做的事') ret = func(*args,**kwargs) print('在被裝飾的函數執行之後做的事') return ret return [email protected] #holiday = wrapper(holiday)def holiday(day): ''' 這是一個放假通知 :param day: :return: ''' print('全體放假%s天'%day) return '好開心'print(holiday.__name__)print(holiday.__doc__)'''結果是inner和None 但我們想要的是打印holiday的字符串格式的函數名和函數的注釋這時該怎麼辦?解決方法就是 from functools import wraps使用語法是@wraps(被裝飾的函數名)'''輸出結果:holiday 這是一個放假通知 :param day: :return:
1、開放封閉原則
1.對原函數的功能擴展是開放的
為什麼要對功能擴展開放呢?
對於任何一個程序來說,不可能在設計之初就已經想好了所有的功能並且未來不做任何更新和修改。所以我們必須允許後來擴展、添加新功能。
2.對修改是封閉的
為什麼要對修改封閉呢?
就像我們剛剛提到的,因為我們寫的一個函數,很有可能在其他地方已經被導入使用了,如果這個時候我們對其進行了修改,很有可能影響其他已經正在使用該函數的代碼。
裝飾器就完美遵循了這個開放封閉原則。這就是學裝飾器的初衷
1、裝飾器的固定格式(模板)
#格式一def timer(func): def inner(*args,**kwargs): '''執行函數之前要做的''' re = func(*args,**kwargs) '''執行函數之後要做的''' return re return inner#格式二from functools import wrapsdef deco(func): @wraps(func) #加在最內層函數正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper
關於“Python裝飾器函數怎麼使用”這篇文章的內容就介紹到這裡,感謝各位的閱讀!相信大家對“Python裝飾器函數怎麼使用”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速雲行業資訊頻道。