先來看乙個不帶引數的裝飾器
1import
time23
deftimeit(fn):
4def wrap(*args,**kwargs):
5 start =time.time()
6 ret = fn(*args,**kwargs)
7print(time.time() -start)
8return
ret9
return
wrap
1011
12@timeit
13def
sleep(x):
14 time.sleep(x)
1 sleep(3)2 3.0034420490264893
這裡列印出來的是執行sleep
函式所消耗的自然時間
,但在執行此函式時所消耗的cpu時間真的有3.0034420490264893秒嗎?當然不是。利用time包中的time.clock
方法可以計算得到**執行所消耗cpu的時間,那怎樣來修改上邊的timeit
函式,讓其即能計算**執行的自然時間,也能計算**執行所消耗cpu的時間?做如下改進:
1def timeit_1(process_time=false):
2 cacl = time.clock if process_time else
time.time
3def
timeit_2(fn):
4def wrap(*args,**kwargs):
5 start =cacl()
6 ret = fn(*args,**kwargs)
7print(cacl() -start)
8return
ret9
return
wrap
10return
timeit_2
1112
13def
sleep_1(x):
14 time.sleep(x)
1 timeit_1(true)(sleep_1)(3)2 0.020000000000000018
1 timeit_1(false)(sleep_1)(3)2 3.0038363933563232
1#引數process_time是乙個預設引數,所以可以不傳遞值,預設為false
2 timeit_1()(sleep_1)(3)
3 3.003509283065796
呼叫過程分解:
1 fn1 = timeit_1(true)
呼叫timeit_1(true)
,函式return回了timeit_2
,並把fn1
這個變數指向了呼叫結果,即指向了timeit_2
,這裡的timeit_2
也是乙個函式,此函式接收乙個引數
1 fn2 = fn1(sleep_1)
呼叫fn1(sleep_1)
,其實就是呼叫了timeit_2(sleep_1)
,並把fn2
這個變數指向了呼叫後的結果,即指向了warp
,這裡的warp
也是乙個函式,此函式能接收任意的引數
1 fn2(3)2 0.009999999999999787
呼叫fn2(3)
,其實是呼叫了wrap(3)
,即執行了wrap
函式內的語句,此函式內的ret = fn(*args,**kwargs)
語句中的fn
其實是指向了sleep
,所以在執行wrap
函式時,sleep_1
函式才真正被執行
改進的裝飾器裝飾乙個函式:
1@timeit_1(false)
2def
sleep_2(x):
3 time.sleep(x)
1 sleep_2(3)2 3.0039477348327637
計算**執行的cpu時間
1@timeit_1(true)
2def
sleep_3(x):
3 time.sleep(x)
1 sleep_3(3)2 0.0
魔法背後的原理:
其實質就是在沒有用魔法的情況下直接timeit_1(true)(sleep_3)(3)
。而當使用@
這個魔法後,當**執行到此行時,解析器會執行timeit_1(true)
,timeit_1
實質就是一函式,接收乙個引數,並返回乙個timeit_2
函式。當**執行到@
所在語句時,會把所裝飾的sleep_3
函式作為乙個引數傳遞給timeit_1(true)
的呼叫結果,即timeit_2
這個函式,即sleep_3
這個函式已作為乙個變數傳遞給了timeit_2(fn)
中的fn
引數,並返回了乙個wrap
函式,在接下的呼叫sleep_3(3)
這個操作,其實此時的sleep_3
這個函式已不是原先的def sleep_3(x):
中的sleep_3
函式,而是乙個指向了wrap
的函式,wrap
函式接收任何引數,所以把當執行sleep_3(3)
時,把引數3
傳遞給了wrap
函式,並執行內部的**,內部**中ret = fn(*args,**kwargs)
中的fn
函式依賴還是指向原先的sleep_3(x)
這個函式。
這裡也有乙個簡單的記憶方式,如果乙個函式被裝飾器所裝飾,在呼叫這個函式時其實不再是呼叫表面上看上去的這個函式,以來做說明
1@timeit_1(true)
2def
sleep_3(x):
3 time.sleep(x)
當執行到有@
魔法所在行時,相當於執行了sleep_3 = timeit_1(true)(sleep_3)
,即指向了wrap
函式,既然sleep_3
指向了wrap
函式,那我們執行sleep_3(3)
時,其實就是在進行wrap(3)
這樣的函式呼叫,記住,函式名也是乙個變數
python裝飾器系列 七
裝飾器 利用 call 方法實現單例 所謂單例,是指乙個類的例項從始至終只能被建立一次。單例的實現有多種,這裡以 call 方法來實現 1 class single 2 instance none34 def init self,cls 5 self.cls cls67 def call self,...
python函式四(裝飾器高階)
一。開放封閉原則 1.對擴充套件是開放的 任何乙個程式,不可能在設計之初就已經想好了所有的功能並且未來不做任何更新和修改。所以我們必須允許 擴充套件 新增新功能。2.對修改是封閉的 比如我們寫的乙個函式,很有可能已經交付給其他人使用了,如果這個時候我們對其進行了修改,很有可能影響其他已經在使用該函式...
python裝飾器 Python 裝飾器
簡言之,python裝飾器就是用於拓展原來函式功能的一種函式,這個函式的特殊之處在於它的返回值也是乙個函式,使用python裝飾器的好處就是在不用更改原函式的 前提下給函式增加新的功能。一般而言,我們要想拓展原來函式 最直接的辦法就是侵入 裡面修改,例如 這是我們最原始的的乙個函式,然後我們試圖記錄...