個人理解:函數裡套函數,返回的函數會賦值給一個變量,這個變量可以在後面被繼續執行調用。
在第一次調用被裝飾函數時進行增強,只增加一次
@+閉包函數名字
裝飾器本質上是一個 Python 函數或類,它可以讓其他函數或類在不需要做任何代碼修改的前提下增加額外功能,裝飾器的返回值也是一個函數/類對象。它經常用於有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景.
Python當中支持任意參數,它寫成*args, **kw。表示的含義是接受任何形式的參數。
**kwargs 會把多傳入的參數以 dict 形式存放(關鍵字參數)
例如:我們定義一個函數(運行無結果,需要調用)
def exp(a, b, c='3', d='f'):
#默認參數即在函數定義階段,就已經為形參賦值,目的是簡化調用,你只需要把必須的參數傳進去。
但是在需要的時候,又可以傳入額外的參數來覆蓋默認參數值。
print(a, b, c, d)
我們可以這樣調用:
args = [1, 3]
dt = {'c': 4, 'd': 5}
exp(*args, **dt)
形參可直接傳入數據(a、b),默認參數用字典dict傳入覆蓋,形式為字典
注意我們傳入list和dict的時候前面加上了*和**,它表示將list和dict當中的所有值展開。如果不加的話,list和dict會被當成是整體傳入。
def func(a,b,c='3',d='f'):
print(a,b,c,d)
args = [1, 0]
func(*args, ) # 輸出1,0,3,f
def func(a,b,c,d): # 都是形參,故調用時傳入為list格式
print(a,b,c,d)
args = [1, 0, 3, 2]
func(*args) # 分隔開,若不添加*則報錯,看做整體傳入
如果我們把函數A和這個函數A的所有參數全部傳入另外一個函數B,以後要實現功能時,只需要調用這個函數B,就可以實現功能,這就相當於代理。
裝飾器的本質其實就是這樣一個agent函數,但是如果使用的時候需要手動傳入會非常麻煩,使用起來不太方便。所以Python當中提供了特定的庫,我們可以讓裝飾器以注解的方式使用,大大簡化操作:
from functools import wraps
def wrapexp(func):
def wrapper(*args, **kwargs):
print('this is a wrapper')
func(*args, **kwargs)
return wrapper
@wrapexp
def exp(a, b, c='3', d='f'):
print(a, b, c, d)
args = [1, 3]
dt = {'c': 4, 'd': 5}
exp(*args, **dt)
在這個例子當中,我們定義了一個wrapexp的裝飾器。我們在其中的wrapper方法當中實現了裝飾器的邏輯,wrapexp當中傳入的參數func是一個函數,wrapper當中的參數則是func的參數。所以我們在wrapper當中調用func(*args, **kw),就是調用打上了這個注解的函數本身。比如在這個例子當中,我們沒有做任何事情,只是在原樣調用之前多輸出了一行’this is a wrapper',表示我們的裝飾器調用成功了。
參考文獻:
Python 函數裝飾器 | 菜鳥教程 (runoob.com)
一文搞定Python裝飾器,看完面試不再慌 - 知乎 (zhihu.com)
1. 什麼是裝飾器
裝飾器本質上是一個 Python 函數或類,它可以讓其他函數或類在不需要做任何代碼修改的前提下增加額外功能,裝飾器的返回值也是一個函數/類對象。它經常用於有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。
2. 使用場景
- 緩存
- 權限校驗(如django中的@login_required和@permission_required裝飾器)
- 性能測試(比如統計一段程序的運行時間)
- 插入日志
3. 用處
在不修改函數內部代碼的前提下,為它包裝一些額外的功能。