在學習裝飾器之前,一定要了解乙個開放封閉原則。軟體開發都應該遵循開放封閉原則。
為什麼說要對擴充套件是開放的呢?因為軟體開發過程不可能一次性把所有的功能都考慮完全,肯定會有不同的新功能需要不停新增。也就是說需要我們不斷地去擴充套件已經存在**的功能,這是非常正常的情況。
那為什麼說對修改是封閉的呢?
比如說已經上線執行的源**,比如某個函式內部的**是不建議直接修改的。因為函式的使用分為兩個階段:函式的定義階段和函式的呼叫階段。因為你不確定這個函式究竟在什麼地方被呼叫了,你如果粗暴的修改了函式內部的源**,對整個程式造成的影響是不可控的。
總結一下就是:不修改源**,不修改呼叫方式,同時還要加上新功能。
從字面上講它也是乙個工具,裝飾其他物件(可呼叫物件)的工具
裝飾器本質上可以是任意可呼叫物件,被裝飾的物件也可以是任意可呼叫物件。
在不修改被裝飾物件源**以及呼叫方式的前提下為它新增新功能。
首先我們先來個例子:
import timeimport random
def index():
time.sleep(random.randrange(
1, 5
)) # 隨機sleep幾秒
print(""
)
index()
現在需求來了,我需要統計下index
函式執行耗費的時間。
你可能會很快寫出下面的**:
import timeimport random
def index():
start_time = time.time()
time.sleep(random.randrange(1, 5)) # 隨機sleep幾秒
stop_time = time.time()
print("耗時{}秒。".format(stop_time - start_time))
index()
輸出:耗時2.0009069442749023秒。
需求是實現了,但是修改了原來index
函式的源**。
那麼我們就要想另外的辦法去實現了。
我們不能修改函式的源**,那麼我們現在能想到的就是定義乙個新的函式來實現這個功能。
類似於這樣:
start_time =time.time()
index() # 在這個函式內部呼叫下index
stop_time =time.time()
print(
"耗時{}秒。
".format(stop_time - start_time))
start_time =time.time()
func() # 在這個函式內部呼叫下func
stop_time =time.time()
print(
"耗時{}秒。
".format(stop_time -start_time))
讓我們來寫一下:
start_time =time.time()
func() # 在這個函式內部呼叫下func
stop_time =time.time()
print(
"耗時{}秒。
".format(stop_time -start_time))
我們現在的問題是:
我通過修改函式名之後,原函式本身沒有引數,但是我定義的新函式內部又用到外面的變數怎麼辦?
用閉包啊!把這個變數做成外部函式的區域性變數:
def timer():輸出:func =index # 在外部函式定義乙個區域性變數
start_time =time.time()
func() # 在這個函式內部呼叫下func
stop_time =time.time()
print(
"耗時{}秒。
".format(stop_time -start_time))
上面的寫法寫死了
func = index
,我們還可以優化下:def timer(func): # 傳乙個func引數和下面func =index一樣都是在my_index函式內部定義了乙個區域性變數
# func =index # 在外部函式定義乙個區域性變數
start_time =time.time()
func() # 在這個函式內部呼叫下func
stop_time =time.time()
print(
"耗時{}秒。
".format(stop_time -start_time))
好了,我們剛剛就已經寫好了乙個裝飾器。
驗證一下,我們的成果:
index = timer(index)
index(
)
耗時1.0029211044311523秒。
沒有修改原函式的源**。
沒有修改原函式的呼叫方式。(雖然此時的index已不再是原來的index,但是並沒有修改呼叫index的方式。)
現在我們來驗證下我們的成果:
import timeimport random
def index():
time.sleep(random.randrange(
1, 5
)) # 隨機sleep幾秒
print(""
)def home():
time.sleep(random.randrange(
1, 5
)) # 隨機sleep幾秒
print(""
)def timer(func): # 傳乙個func引數和下面func =index一樣都是在my_index函式內部定義了乙個區域性變數
start_time =time.time()
func() # 在這個函式內部呼叫下func
stop_time =time.time()
print(
"耗時{}秒。
".format(stop_time -start_time))
return
index =timer(index)
home =timer(home)
index()
home()
輸出:耗時3.0008788108825684秒。
耗時3.00280499458313秒。
簡直完美,功能實現可以裝飾任意函式。就是寫起來有點麻煩。
裝飾器之基本方法
方法你了解多少,不夠清晰的話後面裝飾器可能會糊塗 方法基本模板 def function name parameters.todo return obj元件講解 def 這個是python方法定義的關鍵字,實在說不了啥 function name 自定義方法名,和物件一樣隨便定義,但是函式名不能隨便...
裝飾器之適配裝飾器
可能沒人發現,前面描述的裝飾器受眾面太小了 都是只能知道原函式入參個數的情況下才能編寫裝飾器,所以寫出來的裝飾器都只能針對入參個數的函式使用 不侷限於原函式 def godme fun def godme message print before fun message print after re...
裝飾器基本理論
裝飾器 本質就是函式 功能 為其他函式新增附加功能 原則 1.不修改被修飾函式的源 2.不修改被修飾函式的呼叫方式 1 為函式新增新功能 2import time 3def timmer func 定義裝飾器 45 start time time.time 6 res func args,kwarg...