python裝飾器系列 四

2022-09-16 08:48:12 字數 3705 閱讀 9807

先來看乙個不帶引數的裝飾器

1

import

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的時間?做如下改進:

1

def 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裝飾器的好處就是在不用更改原函式的 前提下給函式增加新的功能。一般而言,我們要想拓展原來函式 最直接的辦法就是侵入 裡面修改,例如 這是我們最原始的的乙個函式,然後我們試圖記錄...