本篇文章給大家?guī)砹岁P(guān)于python的相關(guān)知識,其中主要整理了裝飾器函數(shù)的相關(guān)問題,包括了裝飾器的形成過程、本質(zhì)與功能、進(jìn)階與優(yōu)化等等內(nèi)容,下面一起來看一下,希望對大家有幫助。
推薦學(xué)習(xí):python
假如我寫了一個(gè)函數(shù) f
def f(): print('hello')
之后我想知道這段函數(shù)執(zhí)行所要的時(shí)間,這好辦,我只要將代碼改為如下就行
import time def f(): start = time.time() #獲取程序執(zhí)行開始的時(shí)間 print('hello') end = time.time() #獲取程序執(zhí)行結(jié)束的時(shí)間 print(end - start) #得出函數(shù)f執(zhí)行所要時(shí)間 f()
但之后我有寫了無數(shù)個(gè)函數(shù)f2,f3……fn,我想知道每個(gè)函數(shù)執(zhí)行所需要的時(shí)間,那么如果都像上面一樣改,豈不是很鬧心?還是不行,因?yàn)檫@樣實(shí)在是太麻煩了。那怎么辦呢?于是靈機(jī)一動(dòng),寫了一個(gè)timer函數(shù)。。。
import time def timer(func): start = time.time() func() print(time.time() - start) def f(): print('hello') def f2(): print('xorld') timer(f) timer(f2)
這樣看起來是不是簡單多啦?不管我們寫了多少個(gè)函數(shù)都可以調(diào)用這個(gè)計(jì)時(shí)函數(shù)來計(jì)算函數(shù)的執(zhí)行時(shí)間
但是如果我只想用原來的方式f1(),f2(),fn()調(diào)用了這個(gè)函數(shù),函數(shù)在原本執(zhí)行輸出的結(jié)果不變的前提下還可以增加計(jì)算時(shí)間的功能,而不是調(diào)用timer(f),timer(f2)才能計(jì)算時(shí)間,這該怎么辦呢?
看了下面的裝飾器函數(shù)你就會(huì)知道如何解決這個(gè)問題
一、裝飾器 —— 形成過程
以下就是解決上面問題的代碼的簡單版:
import time def f(): print('hello') def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner f = timer(f) f()
還是這句話我只想用原來的方式f1(),f2(),fn()調(diào)用了這個(gè)函數(shù),函數(shù)在原本執(zhí)行輸出的結(jié)果不變的前提下還可以增加計(jì)算時(shí)間的功能,但我還是要在函數(shù) f 執(zhí)行前寫 f = timer(f)在這一串代碼,是不是覺得礙眼?python的開發(fā)者也覺得礙眼,所以python的開發(fā)者就為我們提供了一句語法糖來解決這個(gè)問題!
二、裝飾器 —— 初識語法糖
用@timmer代替f = timer(f),這就是一句語法糖。
import time def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner @timer #==> 寫著這句話就相當(dāng)于執(zhí)行了f = timer(f) def f(): print('hello') f()
三、裝飾器 ——本質(zhì)與功能
1、本質(zhì)
裝飾器的本質(zhì)就是一個(gè)閉包函數(shù)
2、功能
在不修改原函數(shù)及其調(diào)用方式的情況下對原函數(shù)功能進(jìn)行擴(kuò)展
四、裝飾器 —— 裝飾帶參數(shù),返回值的裝飾器
-
1、裝飾帶一個(gè)參數(shù)的函數(shù)
剛才我們寫的裝飾器都是裝飾不帶參數(shù)的函數(shù),現(xiàn)在要裝飾一個(gè)帶參數(shù)的函數(shù)怎么辦呢?
import time def timer(func): def inner(a): start = time.time() func(a) print(time.time() - start) return inner @timer def f(a): print(a) f('hello')
-
2、裝飾多個(gè)帶有不同參數(shù)但無返回值的函數(shù)
其實(shí)裝飾帶參的函數(shù)并不是什么難事,但假如你有兩個(gè)函數(shù),需要傳遞的參數(shù)不一樣呢,比如 函數(shù)func1有兩個(gè)參數(shù)func1(a ,b),函數(shù)func 2只有一個(gè)參數(shù)func2(a), 且它們都想用這個(gè)裝飾器裝飾,做到計(jì)算函數(shù)執(zhí)行時(shí)間?這怎么辦呢?那就用下面代碼。
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')) 輸出結(jié)果: in func1 0.0 in func2 and get a:aaaaaa 0.0 fun2 over
-
3、裝飾多個(gè)帶有不同參數(shù)且有返回值的函數(shù)
現(xiàn)在參數(shù)的問題已經(jīng)完美的解決了,可是如果你的函數(shù)是有返回值的呢?用上面的代碼你就拿不到返回值了那究竟要如何解決這個(gè)問題呢?那就看下面的代碼吧!
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')) 輸出結(jié)果: in func2 and get a:aaaaaa 0.0 in func2 and get a:aaaaaa 0.0 fun2 over
-
4、多個(gè)裝飾器裝飾同一個(gè)函數(shù)
有些時(shí)候,我們也會(huì)用到多個(gè)裝飾器裝飾同一個(gè)函數(shù)的情況。
ef wrapper1(func): #func ----- f def inner1(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner1 def wrapper2(func): def inner2(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner2 @wrapper2 #f = wrapper2(f) ----->> wrapper2(inner1) == inner2 @wrapper1 #f = wrapper1(f) = inner def f(): print('in f') f() #===>>inner2 #多個(gè)裝飾器裝飾同一個(gè)函數(shù) 輸出結(jié)果: wrapper2 ,before func wrapper1 ,before func in f wrapper1 ,after func wrapper2 ,after func
五、裝飾器 —— 裝飾器進(jìn)階與優(yōu)化
-
1、帶參數(shù)的裝飾器
上面那個(gè)裝飾器已經(jīng)非常beautiful了,但是還有一個(gè)問題,如果我給代碼中無數(shù)個(gè)函數(shù)都加了@timer這個(gè)語法糖,如果之后我又不想用它了那豈不是又要每個(gè)去將它注釋,沒日沒夜忙活3天?豈不是特別麻煩,為了使裝飾器不用時(shí)能夠更好的回收而不是一個(gè)一個(gè)去注釋或者刪除 我們引入帶參數(shù)的裝飾器概念
''' 為了使裝飾器不用時(shí)能夠更好的回收而不是一個(gè)一個(gè)去注釋或者刪除 我們引入帶參數(shù)的裝飾器概念 ''' import time '''FLAGE的目的是用它控制裝飾器的開關(guān), 那么當(dāng)我們不用的時(shí)候就不要一個(gè)一個(gè)去注釋只需將True改為False就行''' FLAGE = True def 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 timmer @timmer_out(FLAGE) #timmer_out(FLAGE) # 也相當(dāng)于執(zhí)行 timmer_out(FLAGE)--->>返回timmer———————>>@timmer(wahaha = timmer(wahaha)) def wahaha(): time.sleep(0.1) #不休息的話函數(shù)執(zhí)行的太快難以計(jì)算時(shí)間 print('wahahahahahaha') wahaha() @timmer_out(FLAGE) def erguotou(): time.sleep(0.1) #不休息的話函數(shù)執(zhí)行的太快難以計(jì)算時(shí)間 print('erguotoutoutou') erguotou() 輸出結(jié)果: wahahahahahaha 0.10152268409729004 erguotoutoutou 0.10795140266418457
-
2、防止函數(shù)必要信息失效
''' print(wahaha.__name__) #查看字符串格式的函數(shù)名 print(wahaha.__doc__) #查看一個(gè)函數(shù)的注釋 ''' #下面用__name__查看holiday的函數(shù)名 from functools import wraps def wrapper(func): @wraps(func) #加在最內(nèi)層函數(shù)正上方 def inner(*args,**kwargs): print('在被裝飾的函數(shù)執(zhí)行之前做的事') ret = func(*args,**kwargs) print('在被裝飾的函數(shù)執(zhí)行之后做的事') return ret return inner @wrapper #holiday = wrapper(holiday) def holiday(day): ''' 這是一個(gè)放假通知 :param day: :return: ''' print('全體放假%s天'%day) return '好開心' print(holiday.__name__) print(holiday.__doc__) ''' 結(jié)果是inner和None 但我們想要的是打印holiday的字符串格式的函數(shù)名和函數(shù)的注釋這時(shí)該怎么辦? 解決方法就是 from functools import wraps 使用語法是@wraps(被裝飾的函數(shù)名) ''' 輸出結(jié)果: holiday 這是一個(gè)放假通知 :param day: :return:
六、裝飾器 —— 裝飾原則
-
1、開放封閉原則
1.對原函數(shù)的功能擴(kuò)展是開放的
為什么要對功能擴(kuò)展開放呢?
對于任何一個(gè)程序來說,不可能在設(shè)計(jì)之初就已經(jīng)想好了所有的功能并且未來不做任何更新和修改。所以我們必須允許后來擴(kuò)展、添加新功能。
2.對修改是封閉的
為什么要對修改封閉呢?
就像我們剛剛提到的,因?yàn)槲覀儗懙囊粋€(gè)函數(shù),很有可能在其他地方已經(jīng)被導(dǎo)入使用了,如果這個(gè)時(shí)候我們對其進(jìn)行了修改,很有可能影響其他已經(jīng)正在使用該函數(shù)的代碼。
裝飾器就完美遵循了這個(gè)開放封閉原則。這就是學(xué)裝飾器的初衷
小結(jié):
-
1、裝飾器的固定格式(模板)
#格式一 def timer(func): def inner(*args,**kwargs): '''執(zhí)行函數(shù)之前要做的''' re = func(*args,**kwargs) '''執(zhí)行函數(shù)之后要做的''' return re return inner #格式二 from functools import wraps def deco(func): @wraps(func) #加在最內(nèi)層函數(shù)正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper
推薦學(xué)習(xí):python