函數裝飾器
1、簡單裝飾器
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
2、帶參數的裝飾器
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
3、自定義參數的裝飾器
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
4、原函數還是原函數嗎
試着打印出 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
通常使用緩存裝飾器,來包裹這些檢查函數,避免其被反復調用,進而提高程序運行效率,比如寫成下面這樣