python函式裝飾器和類裝飾器
阿新 • • 發佈:2020-08-24
目錄
函式裝飾器
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
通常使用快取裝飾器,來包裹這些檢查函式,避免其被反覆呼叫,進而提高程式執行效率,比如寫成下面這樣