初學該怎樣理解python裝飾器

2021-10-06 09:44:36 字數 4085 閱讀 8612

記得以前學到這裡,看了很多關於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...