函數裝飾器
-
簡單裝飾器
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper def greet(): print('hello world') greet = my_decorator(greet) greet() # 輸出 # wrapper of decorator # hello world
上述代碼在 Python 中有更簡單、更優雅的表示:
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper @my_decorator def greet(): print('hello world') greet() # 輸出 # wrapper of decorator # hello world
-
帶參數的裝飾器
def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper @my_decorator def greet(message): print(message) greet('hello world') # 輸出 # wrapper of decorator # hello world
-
自定義參數的裝飾器
def repeat(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator {}'.format(i)) func(*args, **kwargs) return wrapper return my_decorator @repeat(4) def greet(message): print(message) greet('hello world') # 輸出: # wrapper of decorator 0 # hello world # wrapper of decorator 1 # hello world # wrapper of decorator 2 # hello world # wrapper of decorator 3 # hello world
-
原函數還是原函數嗎?
試着打印出 greet() 函數的一些元信息:
greet.__name__ ## 輸出 'wrapper' help(greet) # 輸出 Help on function wrapper in module __main__: wrapper(*args, **kwargs)
greet()
函數被裝飾以后,它的元信息變了。元信息告訴我們“它不再是以前的那個greet()
函數,而是被wrapper()
函數取代了”。為了解決這個問題,通常使用內置的裝飾器
@functools.wrap
,它會幫助保留原函數的元信息(也就是將原函數的元信息,拷貝到對應的裝飾器函數里)。import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper @my_decorator def greet(message): print(message) greet.__name__ # 輸出 'greet'
類裝飾器
實際上,類也可以作為裝飾器。類裝飾器主要依賴於函數__call__()
,每當你調用一個類的示例時,函數__call__()
就會被執行一次。
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs)
@Count
def example():
print("hello world")
example()
# 輸出
num of calls is: 1
hello world
example()
# 輸出
num of calls is: 2
hello world
我們定義了類 Count,初始化時傳入原函數 func()
,而__call__()
函數表示讓變量 num_calls 自增 1,然后打印,並且調用原函數。因此,在我們第一次調用函數 example()
時,num_calls 的值是 1,而在第二次調用時,它的值變成了 2
裝飾器的應用
-
身份認證 authenticate
-
日志記錄
-
輸入合理性檢查 validation_check
-
緩存 lru_cache
通常使用緩存裝飾器,來包裹這些檢查函數,避免其被反復調用,進而提高程序運行效率,比如寫成下面這樣
@lru_cache def check(param1, param2, ...) # 檢查用戶設備類型,版本號等等 ...