python閉包函式與裝飾器
阿新 • • 發佈:2022-03-18
目錄
閉包函式
閉包概念
- 閉:定義在函式內部的函式
- 包:內部函式使用了外層函式名稱空間中的名字
# 閉包函式
def outer():
x = 111
# 定義在函式內部的函式
def inner():
# 使用了外層函式名稱空間中的名字
print(x)
return inner
x = 666
res = outer()
res() # 輸出:111
實際應用
閉包函式是給函式體傳參的另外一種方式
def outer(username):
def index():
print(username)
return index
res = outer('kevin') # 形參username與值kevin臨時繫結
res() # 輸出:kevin
res1 = outer('jason') # 形參username與值jason臨時繫結
res1() # 輸出:jason
這時候有人就很迷惑了,明明只要定義一個函式index()就可以了,為什麼要用outer()包起來傳參呢?別急,這是為接下來的內容做鋪墊用的。
裝飾器
簡介
裝飾器並不是一個新的知識點,而是由前面所有的函式知識點整合到一起的產物。
裝飾器的本質:在不改變被裝飾物件原有的“呼叫方式”和“內部程式碼”的情況下給被裝飾物件新增新的功能。
這是不是聽起來有些神奇?裝飾器的原則就是對擴充套件開放,對修改封閉。
簡單版本裝飾器
瞭解完什麼是裝飾器之後,我們先學習使用簡單版本的裝飾器。
現在有一段程式碼:
import time # 匯入模組,不用管什麼意思
def index():
time.sleep(1) # 讓程式休眠1秒鐘
print('這裡是index函式')
def home():
time.sleep(1) # 讓程式休眠1秒鐘
print('這裡是home函式')
我如何能在不動上面2個函式的情況下,新增一個功能:輸出函式執行的時長。
這時候有人就要說了:啊?這不是很簡單,我只要新增一個函式
def get_time(function_name):
start_time = time.time() # 獲取執行函式前的時間
function_name() # 呼叫函式
end_time = time.time() # 獲取執行結束後的時間
print(end_time - start_time) # 輸出時間差
get_time(index)
get_time(home)
這個方法確實是可以,但是執行函式都要呼叫get_time()。
所以彆著急,裝飾器的功能還沒了解全之前,請先繼續看下去。
實現:
import time # 匯入模組,不用管什麼意思
# 裝飾物件
def index():
time.sleep(1) # 讓程式休眠1秒鐘
print('這裡是index函式')
def home():
time.sleep(1) # 讓程式休眠1秒鐘
print('這裡是home函式')
# 裝飾器
def outer(function_name):
def inner():
# 獲取執行函式前的時間
start_time = time.time()
# 呼叫名為function_name函式
function_name()
# 獲取函式執行結束後的時間
end_time = time.time()
# 輸出時間差
print(end_time - start_time)
# 返回內部函式名
return inner
# 讓index()變成inner()
index = outer(index)
index()
"""
輸出:
這裡是index函式
1.0101792812347412
"""
# 狸貓換太子
home = outer(home)
home()
"""
輸出:
這裡是home函式
1.0047881603240967
"""
這個時候函式只需要呼叫它自己就能輸出執行時間了,相當於給函式內部添加了一段程式碼一樣。
進階版本裝飾器
這時候如果函式有引數該怎麼做呢?並且引數還不知道幾個?
很簡單,只要加上可變長形參就行了。
實現:
import time
def index(a): # 有引數
time.sleep(1)
print('這裡是index函式', a)
def home(): # 無引數
time.sleep(1)
print('這裡是home函式')
def outer(function_name):
def inner(*args, **kwargs): # 新增可變長形參
start_time = time.time()
function_name(*args, **kwargs) # 傳參
end_time = time.time()
print(end_time - start_time)
return inner
index = outer(index)
index(555)
"""
輸出:
這裡是index函式 555
1.0047197341918945
"""
home = outer(home)
home()
"""
輸出:
這裡是home函式
1.0003490447998047
"""
完整版本裝飾器
這個時候問題又出現了,如果函式有返回值又該怎麼辦?
也很簡單,在裝飾器內部函式加一個return返回值就行了。
實現:
import time
def index(a):
time.sleep(1)
print('這裡是index函式')
return a # 有返回值
def home():
time.sleep(1)
print('這裡是home函式')
def outer(function_name):
def inner(*args, **kwargs):
start_time = time.time()
# 將函式返回值賦值給res
res = function_name(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
"""返回函式的返回值"""
return res
return inner
index = outer(index)
print(index(555))
"""
輸出:
這裡是index函式
1.0073633193969727
555
"""
home = outer(home)
home()
"""
輸出:
這裡是home函式
1.0051474571228027
"""
裝飾器模板
還沒搞明白裝飾器怎麼回事?小問題!這裡有一套萬能模板,只要知道怎麼使用裝飾器就行了。
'''編寫裝飾器其實有一套固定的程式碼 不需要做任何理解'''
def outer(func_name): # func_name用於接收被裝飾的物件(函式)
def inner(*args, **kwargs):
print('執行被裝飾函式之前 可以做的額外操作')
res = func_name(*args, **kwargs) # 執行真正的被裝飾函式
print('執行被裝飾函式之後 可以做的額外操作')
return res # 返回真正函式的返回值
return inner
裝飾器語法糖
由於感覺在這塊程式碼太繁瑣了
index = outer(index)
index()
於是python給了一個方法,用@+裝飾器名稱放在裝飾物件的上方。
實現:
def outer(func_name):
def inner(*args, **kwargs):
# 額外操作
res = func_name(*args, **kwargs)
# 額外操作
return res
return inner
# 裝飾器語法糖
@outer
def index():
print('這裡是index函式')
"""可以直接使用index(),省去了一個步驟"""
index()
語法糖內部原理:
- 使用的時候最好緊跟在被裝飾物件的上方
- 語法糖會自動將下面緊挨著的函式名傳給@後面的函式呼叫
裝飾器修復技術
在瞭解裝飾器修復技術之前,我們先了解被裝飾物件的內部屬於誰了
def outer(func_name):
def inner(*args, **kwargs):
res = func_name(*args, **kwargs)
return res
return inner
@outer
def index():
print('這裡是index函式')
print(index)
"""
輸出:
<function outer.<locals>.inner at 0x0000025BA5B9F268>
"""
可以看出,這時的index已經是inner的人了,這就讓人很不爽了,所以我們要把它恢復回來。
from functools import wraps # 匯入模組
def outer(func_name):
@wraps(func_name) # 修復
def inner(*args, **kwargs):
res = func_name(*args, **kwargs)
return res
return inner
@outer
def index():
print('這裡是index函式')
print(index)
"""
輸出:
<function index at 0x00000133924FF268>
"""
可以看到,index還是原來的index。