記得以前學到這裡,看了很多關於python裝飾器的文章,對於裝飾器還是一知半解,後來在一些例項中, 通過最樸實的方法總算是摸清楚了什麼是裝飾器?以下是個人對裝飾器的筆記。
在學習裝飾器之前,我們必須搞清楚一點,裝飾器究竟是什麼? 用文字說明, 裝飾器實際上就是為了給某個函式,或者稱某個程式新增乙個功能。並且考慮 到該函式或程式已經被引用,所以我們不能對函式或程式本身進行修改,所以 有了裝飾器,可以想象成,我們人拿上斧頭,擁有了砍樹的能力,這就是裝飾器的作用。
裝飾器需要滿足以下兩點:
不修改原程式或函式的**
不改變函式或程式的呼叫方法
ps:此處如果不理解,我們還用人砍樹的例子。比如說小明他是個人,你要讓他砍樹,他怎麼砍, 必須要讓他擁有砍樹能力,那麼問題來了,怎麼讓他擁有這個能力呢?好吧可以給他注射藥劑, 讓他變強,但是這就不滿足上述第一點了,被改造後的小明我們就不認識他了,不知道怎麼讓他去砍樹, 所以就必須有前提條件1,不得修改源**。 第二條,不改變呼叫方法。原本把我想讓小明去砍樹就對 小明下命令,「嘿小明去砍個樹」,但是小明不會啊,怎麼辦呢,讓乙個會的人帶著小明去,這個過程就成了什麼呢? 你讓某個人帶著小明去,而不是你讓小明去,這就出現了呼叫方法的改變。
第一步:裝飾器的前奏
定義乙個測試的函式,返回輸入的值
def
test1
(a):
return a
上述定義的函式,它的作用就是返回輸入值,那麼接下來要考慮給它加乙個計算它自身執行時間的功能。最簡單的思路是,記錄開始時間->執行函式->記錄結束時間, 用結束時間減去開始時間就是我們想要的結果了,如下:
import time
start_time = time.time(
)# 開始時間
test1(
250)
# 要測試的函式
stop_time = time.time(
)# 結束時間
delta_time = stop_time - start_time # 時間間隔,即執行時間
好了,簡單地實現了。根據需求應該把這個過程封裝在函式內,呼叫一次就返回test1的執行時間。塞到函式裡面大概是這樣:
def
test2()
start_time = time.time(
)# 開始時間
test1(
250)
# 要測試的函式
stop_time = time.time(
)# 結束時間
delta_time = stop_time - start_time # 時間間隔,即執行時間
print(''
.format
(delta_time)
)# 格式化列印
執行一下
>>
> test2(
250)
'0.000001'
此處可以看到,將上述過程封裝到 test2 之後,通過呼叫 test2 就能得到 test1 函式的執行時間, 但是不難發現有兩個點:
(1)改變了呼叫方法,根據裝飾器的描述,應該直接呼叫test1就得到執行時間
(2)此處只研究了test1(250)也就是說此時test1只有乙個固定引數,這不能保證是否會因為引數的不同執行時間不同。
針對這兩個點進一步改進
首先來解決一下關於呼叫的時候不再是固定引數,如下:
def
test2
(func)
:def
test3
(x):
start_time = time.time(
)# 開始時間
func(x)
# 要測試的函式
stop_time = time.time(
)# 結束時間
delta_time = stop_time - start_time # 時間間隔,即執行時間
return''.
format
(delta_time)
# f返回格式化時間
return test3
deftest1
(a):
return a
執行一下
>>
> test2(test3)
(250
)'0.000002'
現在有三個函式test1, test2, test3。整個流程還是得梳理清楚,是為了計算test1的執行時間, 那麼還是"記錄開始時間->執行函式->記錄結束時間"這乙個流程。
在上面這段**中,首先把test1的函式位址當成引數傳入到了test2, 此時test2(func)相當於test2(test1),在test2中,再定義乙個test3。把上述求執行時間的流程放進去,在test3中可以看到乙個func(x),func是我們傳入的位址也就是test1,要計算test1的執行時間,那肯定是需要呼叫的,func()就是func的呼叫,func(x)相當於test1(x)。
再來看test2函式的返回值,是test3的位址,一方面不會因為呼叫而報錯,只是拿了test3函式的位址; 一方面是為了取得test3以保證後面的展開。現在目的就很明確了。呼叫test2, 傳入test1的位址,又因為是返回了test3的位址,實際上經過一頓操作,test2(test1)就像是test3的位址,test2(test1)()就是在例項化test3了, 即test3(), 顯而易見,其中 x 即最初傳入的 250 。現在可以這樣來獲得各種引數中test1的執行時間。
第二步,實現裝飾器
剛剛說到兩個點,已經解決了乙個,再去解決呼叫方法。在test1函式加乙個@test2,這是乙個語法糖, 效果如下:
def
test2
(func)
:def
test3
(x):
start_time = time.time(
)# 開始時間
func(x)
# 要測試的函式
stop_time = time.time(
)# 結束時間
delta_time = stop_time - start_time # 時間間隔,即執行時間
return''.
format
(delta_time)
# f返回格式化時間
return test3
@test2
deftest1
(a):
return a
執行一下
>>
> test1(
250)
'0.000001'
加入語法糖之後直接呼叫test1就得出了想要的結果。那麼這個語法糖的作用就顯而易見了。 就是替代test2(test1)的。
就是說test2(test1)(250)直接被語法糖省略成了test1(250),這二者是相同的效果,但是滿足了不改變呼叫方法,而且我們自始至終都沒有修改過test1裡面的源**。
這就是裝飾器。
初學Python 裝飾器
當我們做好乙個產品之後,需要對它進行不斷地維護,對某些函式增加一些功能。這個時候如果去修改源 將是非常不合適的。原因 1.原則上已經寫好的函式盡量不去修改它,因為一旦修改可能會導致不可預知的錯誤發生或者降低穩定性。2.函式可能被呼叫很多很多次,如果修改函式有可能會導致呼叫方式發生改變,會有大量的修改...
python裝飾器理解 python裝飾器理解
裝飾器 在不改變原函式的 和呼叫方法的基礎上,給原函式增加額外的功能 理解宣告 為了方便理解,以下例子採用最簡潔的函式和新增的功能 給原函式新增乙個執行時間 import time def timer func def inner func return inner timer func timer...
python裝飾器 理解Python裝飾器
在python中,對於乙個函式,若想在其執行前後做點什麼,那麼裝飾器是再好不過的選擇,話不多說,上 usr bin env coding utf 8 script 01.py author howie from functools import wraps def decorator func wr...