裝飾器詳解
裝飾器,顧名思義就是裝飾的意思,就是不改變裏面的牆體結構,只是在外面增加了一層隔音板或者牆紙。
對應程序也是這樣的,不改變原函數的結構功能,調用函數的接口也沒有變化,但是就是增多了該函數的功能。這是怎麼做到的呢?本次用一個例子來全面說明。
我們已經寫好了一個函數是index
,現在給該函數index
加上該函數運行的時間的功能。
1、原始代碼
# -*- coding=utf-8 -*-
import time
def index():
time.sleep(2)
startTime = time.time()
index() #f1,f2...fn
endTime = time.time()
print(endTime - startTime) #輸出2.000297784805298
上面代碼表示運行index
函數花費的時間,這就有一個問題了,如果我要計算函數f1()
,函數f2()
,函數fn()
…那麼每一個函數都要這麼寫,就會重複很多代碼,這是寫程序不允許的,因此需要一個函數來進行包裝,也就是說decorator就是一個返回函數的高階函數。
2、增加函數
import time
def index():
time.sleep(2)
def calculate_time():
startTime = time.time()
index()
endTime = time.time()
print(endTime - startTime)
calculate_time() #out:2.000297784805298
上面這麼寫就是用了一個函數,還是不能解決之前提到的問題,換了index()
,還得重新寫calculate_time
函數,那麼此時我們需要用到將函數傳遞到函數裏面。
# -*- coding=utf-8 -*-
import time
def index():
time.sleep(2)
def calculate_time(f):
startTime = time.time()
f()
endTime = time.time()
print(endTime - startTime)
calculate_time(index)
把index()
傳過去,那麼之後需要修改index
就很方便,但是這個也不能完全解決之前的問題,即我改變的函數的調用接口,之前的函數是index
,現在的函數變成了calculate_time
,這也也很不習慣,因此我們需要進一步升級。
3、加入閉包
這一步就是在函數裏面添加函數
import time
def index():
time.sleep(2)
def calculate_time(f):
def inner():
startTime = time.time()
f()
endTime = time.time()
print(endTime - startTime)
return inner
index = calculate_time(index)
index()
這個寫法和上面的例子是一致的,且調用的時候,我們只是用了index()
,這就是裝飾器了,不過需要在前面加一句話index = calculate_time(index)
所以我們需要python中的語法糖@
# -*- coding=utf-8 -*-
import time
def calculate_time(f):
def inner():
startTime = time.time()
f()
endTime = time.time()
print(endTime - startTime)
return inner
@calculate_time #index = calculate_time(index)
def index():
time.sleep(2)
index()
需要知道 @calculate_time
等價於 index = calculate_time(index)
4、加入輸出變量
一般函數都是由return
輸出的,本例子加入index
輸出結果,如下:
import time
def calculate_time(f):
def inner():
startTime = time.time()
result = f() #f函數是有輸出的,符合一般的函數寫法
endTime = time.time()
print(endTime - startTime)
return result
return inner
@calculate_time #index = calculate_time(index)
def index():
time.sleep(2)
return 'index'
res = index()
print(res) # out:2.0000038146972656 index
5、升級優化傳遞函數
如果我們需要在寫入一個其他的函數,且各個函數輸入不一樣,那麼此時需要對函數的傳遞參數進行改進,如下:
# -*- coding=utf-8 -*-
import time
def calculate_time(f):
def inner(*args,**kwargs):
startTime = time.time()
result = f(*args,**kwargs)
endTime = time.time()
print(endTime - startTime)
return result
return inner
@calculate_time
def index():
time.sleep(2)
return 'index'
@calculate_time
def add(x,y):
time.sleep(2)
return x + y
res = index()
addRes = add(1,2)
print(res,addRes)
輸出:
2.000779151916504
2.000826597213745
index 3
把*args
,**kwargs
傳遞到函數裏面,之後就可以隨意寫自己需要被裝飾的函數了。
6、總結
一個標準的裝飾器如下:
def calculate_time(f):
def inner(*args,**kwargs):
startTime = time.time()
result = f(*args,**kwargs)
endTime = time.time()
print(endTime - startTime)
return result
return inner
先把功能寫好,比如上面的計算時間差。然後調用我們的需要被裝飾的函數f(*args,**kwargs)
,並給出一個返回值,且需要把內層函數作爲結果返回出去。那麼最後我們的 index = calculate_time(index)
等價於@calculate_time
。
整個便是裝飾器的內容了。
記住: @calculate_time
等價於 index = calculate_time(index)