標籤:python python函數裝飾器 裝飾器 python裝飾器
一、裝飾器定義:
1.裝飾器的本質為函數;
2.裝飾器是用來完成被修飾函數的附加功能的
所以:裝飾器是用來完成被修飾函數附屬功能的函數
裝飾器的要求:
1.不能修改被修飾函數的原始碼;
2.不能更改被修飾函數的運行方式;
3.上述兩者缺一不可。
二、裝飾器的構成:
裝飾器=高階函數+函數嵌套+閉包
裝飾器的簡單架構:
import time
#裝飾器的簡單架構
def run_time(fun):#傳入參數為函數
def wrapper():#函數嵌套
fun() #閉包(函數範圍)
return wrapper #返回wrapper函數名
#要求完成一個裝飾器,用來計算函數的已耗用時間
#高階函數
def run_time(fun):
def wrapper():
startime = time.time()
fun()
endtime = time.time()
print("函數已耗用時間為%s"%(endtime-startime))
return wrapper
#被裝飾的函數
def test():
time.sleep(2)
#完成裝飾功能
test = run_time(test) #運行run_time函數,返回test函數的記憶體位址
test() #運行test函數
截止目前為止,既未改變test函數的原始碼又未改變test函數的運行方式,還為test函數加上了計算它已耗用時間的功能,符合裝飾器的要求。
三、最佳化使用br/>Python中直接使用@符號來調用裝飾器,例如:
#被裝飾的函數
def test():
time.sleep(2)
#完成裝飾功能
test = run_time(test) #運行run_time函數,返回test函數的記憶體位址
test() #運行test函數
@run_time
def test():
time.sleep(2)
test()
兩者的功能完全相同,即@run_time與test = run_time(test)的代碼塊功能完全相同
四、加入傳回值
若想在test函數中加上傳回值,怎麼辦?
首先按照最常規的辦法來處理,代碼如下:
@run_time #相當運行wrapper
def test1():
time.sleep(2)
return "test1函數運行完畢"
test1()
可以發現,並沒有返回我們想要的結果(test1函數運行完畢),為什麼呢?從上面內容可知,我們在運行test1函數時,實際上是在調用run_time函數,最終也是在調用warpper函數,此時,返回的結果wrapper函數中的結果。但wrapper函數中並沒有傳回值,所以上面的裝飾器沒有得到我們想要的結果。現進行改進,代碼塊如下:
#裝飾器的簡單架構
def run_time(fun):
def wrapper():
startime = time.time()
res = fun()
endtime = time.time()
print("函數已耗用時間為%s"%(endtime-startime))
return res
return wrapper
@run_time #相當運行wrapper
def test2():
time.sleep(2)
return "test1函數運行完畢"
res = test2()
print(res)
運行結果如:
五、帶參數函數的裝飾器
我們知道,大部分的函數都是有參數的,沒有參數的函數一般意義不大。
那麼,如何用裝飾器來修飾函數呢?
首先,我們已經明白了上面的例子。在運行函數test時,實際上就是裝飾器函數中的嵌套函數warpper,同時這個裝飾器的功能可以附加給任意函數,也就是說test函數時可變的,即test函數的參數不定,如何能夠讓warpper能夠接受任意被修飾函數(test)的參數呢?我們知道,為瞭解決此問題,python在函數中應用了類似元組和字典的方式來存放任意數量函數的位置參數和關鍵字參數。這樣我們就可以很好解決修飾器中的參數問題了,見下面代碼塊:
#帶參數的函數裝飾器
#裝飾器的簡單架構
def run_time(fun):
def wrapper(*args,*kwargs):
startime = time.time()
res = fun(args,**kwargs)
endtime = time.time()
print("函數已耗用時間為%s"%(endtime-startime))
return res
return wrapper
@run_time #相當運行wrapper
def test2(name,age):
time.sleep(2)
print("姓名為:%s,年齡為:%s"%(name,age))
return "test1函數運行完畢"
@run_time #相當運行wrapper
def test3(name,age,sex):
time.sleep(2)
print("姓名為:%s,年齡為:%s,性別為:%s"%(name,age,sex))
return "test1函數運行完畢"
test2(‘zhang‘,24)
test3(‘zhang‘,24,sex=‘man‘)
運行結果如:
六、帶參數的裝飾器
裝飾器也是函數,所以當有需要的時候,裝飾器也是可以帶參數的。那麼如何使用帶參數的裝飾器呢?我們從上面的例子中知道,裝飾器函數(run_time)的參數是func(即為所要裝飾的函數),那麼此處肯定不能帶參數了。如何解決這個問題呢?還記得前面講到的裝飾器是由高階函數+函數嵌套+閉包組成的嗎?此處就要用到閉包的概念了,閉包可以理解成變數的範圍。我們知道,在使用函數嵌套時,變數是使用LEGB的原則(http://blog.51cto.com/10836356/2094956)。這樣我們就可以使用以下代碼來解決該問題了,代碼塊如下:
#帶參數的裝飾器
#裝飾器的簡單架構
def type_sex(sex):
if sex ==‘man‘:
def run_time(fun):
def wrapper(*args,kwargs):
startime = time.time()
res = fun(*args,*kwargs)
endtime = time.time()
print("函數已耗用時間為%s"%(endtime-startime))
return res
return wrapper
return run_time
else:
def run_time(fun):
def wrapper(args,kwargs):
res = fun(*args,**kwargs)
endtime = time.time()
print("該函數運行完的時間為%s"%(endtime))
return res
return wrapper
return run_time
@type_sex(sex=‘man‘) #相當運行wrapper
def test5(name,age):
time.sleep(2)
print("姓名為:%s,年齡為:%s"%(name,age))
return "test1函數運行完畢"
@type_sex(sex=‘woman‘) #相當運行wrapper
def test6(name,age,sex):
time.sleep(2)
print("姓名為:%s,年齡為:%s,性別為:%s"%(name,age,sex))
return "test1函數運行完畢"
test5("zhang",24)
test6("zhou",26,‘woman‘)
運行結果如:
七、裝飾器函數的運行過程
Python函數裝飾器