一個函數被定義完成後,甚至程序發佈後,後期可能需要添加某些功能,但是我們不可能每次都去修改原函數的代碼,這時候裝飾器就可以上場了,本篇文章將會用一個個可實現的代碼,由淺入深、循序漸進得闡述裝飾器的強大之處
裝飾器定義、作用、原則:
- 定義: 一個裝飾函數的函數,即爲其他函數添加附加功能
- 作用: 給函數添加新功能,但是原來的函數代碼不會被更改
- 原則: 不修改被修飾函數的源代碼,不修改被修飾函數的調用方式
裝飾器=高階函數+函數嵌套+閉包
- 高階函數: 函數接收的參數是一個函數名 或者 函數的返回值是一個函數名
- 未使用裝飾器:
import time
def test(s):
start_time = time.time()
res = 0
for i in s:
time.sleep(0.1) #沉睡10秒
res += i
stop_time = time.time()
print('函數的運行時間是{}秒'.format(stop_time - start_time))
return res
print(test(range(100))) #運行函數,傳入一個從0到100的範圍
運行結果:
函數的運行時間是10.012572765350342秒
4950
- 高階函數:
import time
def foo():
time.sleep(3)
print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def test(func): #傳入的參數是一個函數,即爲高階函數
print(func) #輸出函數的內存地址
func() #調用形參函數
test(foo) #調用高階函數
運行結果:
<function foo at 0x02BF7B70>
大海如此寬廣,總有一天你會遇見真心的夥伴
- 帶修飾的高階函數(不修改原函數源代碼):
import time
def foo():
time.sleep(3)
print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def test(func): #傳入的參數是一個函數,即爲高階函數
start_time = time.time()
print(func) #輸出函數的內存地址
func() #調用形參函數
stop_time = time.time()
print('函數運行時間爲 %s' %(stop_time - start_time))
test(foo) #調用高階函數
運行結果:
<function foo at 0x004DC5D0>
大海如此寬廣,總有一天你會遇見真心的夥伴
函數運行時間爲 3.0281732082366943
- 帶修飾的高階函數(不修改原函數調用方式):
import time
def foo():
time.sleep(3)
print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def test(func): #傳入的參數是一個函數,即爲高階函數
return func #函數的返回值爲函數名,也稱爲高階函數
foo = test(foo) #用foo接收test運行的返回值
foo()
運行結果:
大海如此寬廣,總有一天你會遇見真心的夥伴
- 不修改源代碼也不修改調用方式:
import time
def foo():
time.sleep(3)
print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def timmer(func): #作爲一個修飾函數
start_time = time.time()
func()
stop_time = time.time()
print('函數運行時間爲 %s' %(stop_time - start_time))
return func #函數的返回值爲函數名,也稱爲高階函數
foo = timmer(foo) #將原函數名作爲接收的變量,被修飾後,就不會修改原函數的調用方式
foo()
#這樣調用後,其實是會連帶被修飾的結果一起輸出,因爲上一句賦值時已經調用了修飾函數
運行結果:
大海如此寬廣,總有一天你會遇見真心的夥伴
函數運行時間爲 3.018172264099121
大海如此寬廣,總有一天你會遇見真心的夥伴
但是這樣,它僅僅用了高階函數,還是滿足不了裝飾器的功能,因爲它多運行了一步
- 函數嵌套:
一個函數裏定義多個函數(這裏需要用到函數作用域和函數嵌套的知識,在我的這篇文章裏有從淺入深的詳細分析:初學者python筆記(函數))
def father(name):
#name = '白鬍子_1' #三個地方對局部變量的修改
def son():
#name = '白鬍子_2'
def grandson():
#name = '白鬍子_3'
print('我的老爹是{}'.format(name)) #離它最近的局部變量的修改
grandson()
son() #運行嵌套函數
father('白鬍子')
#裏面的變量是一個一個層級的關係,從形參,可以一直傳到底層的函數中再調用
運行結果:
我的老爹是白鬍子
- 基本裝飾器的實現(帶語法糖):
import time
def timmer(func):
def wrap():
start_time = time.time() #給裝飾器加的功能
func() #在裝飾器中運行函數
stop_time = time.time()
print('函數運行了{}秒'.format(stop_time - start_time))
return wrap #函數的返回值是函數,所以爲高階函數
@timmer #語法糖的使用,使原函數即foo函數被裝飾。相當於foo = timmer(foo)語句
def foo():
time.sleep(3)
print('test函數運行完畢!')
foo() #運行原函數,方式不變,滿足原則
運行結果:
test函數運行完畢!
函數運行了3.0251729488372803秒
- 能得到原函數返回值的裝飾器:
import time
def timmer(func):
def wrap():
start_time = time.time() #給裝飾器加的功能
res = func() #在裝飾器中運行函數,並且用一個變量來接收以返回原函數的返回值
stop_time = time.time()
print('函數運行了{}秒'.format(stop_time - start_time))
return res #返回原函數的返回值
return wrap #函數的返回值是函數,所以爲高階函數
@timmer #語法糖的使用,使原函數即foo函數被裝飾。相當於foo = timmer(foo)語句
def foo():
time.sleep(3)
print('test函數運行完畢!')
return '這是test的返回值' #爲了實現把原函數的返回值也接收到
foo() #運行原函數,方式不變,滿足原則
print(foo()) #輸出原函數的返回值
運行結果:
test函數運行完畢!
函數運行了3.0211730003356934秒
test函數運行完畢!
函數運行了3.0581750869750977秒
這是test的返回值