函數 -> 裝飾器
函數的4個核心概念
1.函數可以賦與變量
def func(message): print('Got a message: {}'.format(message)) send_message = func send_message('hello world') #輸出 #Got a message: hello world
2.函數可以當作函數的參數
def get_message(message): return 'Got a message: ' + message def root_call(func, message): print(func(message)) root_call(get_message, 'hello world') 輸出 #Got a message: hello world
3.函數里嵌套函數
def func(message): def get_message(message): print('Got a message: {}'.format(message)) return get_message(message) func('hello world') 輸出 #Got a message: hello world
4.函數作為函數返回值(閉包)
def func_closure(): def get_message(message): print('Got a message: {}'.format(message)) return get_message send_message = func_closure() send_message('hello world') #輸出 #Got a message: hello world
簡單裝飾器
例
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper def greet(): print('hello world') greet = my_decorator(greet) greet()
使用語法糖 @
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
帶有參數的裝飾器
直接在 wrapper函數中加上參數
def my_decorator(func): def wrapper(message): print('wrapper of decorator') func(message) return wrapper @my_decorator #相當於 greet == wrapper(message) def greet(message): print(message) greet('hello world') # 輸出 #wrapper of decorator #hello world
這個裝飾器只能用在有一個參數的函數,如果想對任意參數的函數通用,可以這么寫
def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper
帶自定義參數的裝飾器
利用裝飾器自定義參數這特性,實現重復執行裝飾器內部函數
def repeat(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator') func(*args, **kwargs) return wrapper return my_decorator @repeat(4) def greet(message): print(message) greet('hello world') # 輸出: # wrapper of decorator # hello world # wrapper of decorator # hello world # wrapper of decorator # hello world # wrapper of decorator # hello world
原函數還是原函數?
greet.__name__ #輸出 'wrapper' help(greet) # 輸出 Help on function wrapper in module __main__: wrapper(*args, **kwargs)
可以看出,原函數的原信息會被wrapper取代
如果不想其改變,那么可用內置裝飾器@functools.wraps將原函數的元信息拷貝過去。
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
裝飾器的嵌套
@decorator1 @decorator2 @decorator3 def func(): ... #相當於 decorator1(decorator2(decorator3(func)))
import functools def my_decorator1(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator1') func(*args, **kwargs) return wrapper def my_decorator2(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator2') func(*args, **kwargs) return wrapper @my_decorator1 @my_decorator2 def greet(message): print(message) greet('hello world') # 輸出 # execute decorator1 # execute decorator2 # hello world
裝飾器的實例用法
1)身份驗證,不登錄不允許操作
import functools def authenticate(func): @functools.wraps(func) def wrapper(*args, **kwargs): request = args[0] if check_user_logged_in(request): # 如果用戶處於登錄狀態 return func(*args, **kwargs) # 執行函數 post_comment() else: raise Exception('Authentication failed') return wrapper @authenticate def post_comment(request, ...) ...
2)日志記錄 可測試函數的執行時間
import time import functools def log_execution_time(func): def wrapper(*args, **kwargs): start = time.perf_counter() res = func(*args, **kwargs) end = time.perf_counter() print('{} took {} ms'.format(func.__name__, (end - start) * 1000)) return res return wrapper @log_execution_time def calculate_similarity(items): ...
3) 合法性檢測
import functools def validation_check(input): @functools.wraps(func) def wrapper(*args, **kwargs): ... # 檢查輸入是否合法 @validation_check def neural_network_training(param1, param2, ...): ... LRU cache. @lru_cache緩存裝飾器 @lru_cache def check(param1, param2, ...) # 檢查用戶設備類型,版本號等等 ...
4) try...excaption
class ServerDebugHelper: @classmethod def debug(cls): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except: import traceback print(traceback.format_exc()) return wrapper return decorator
參考
極客時間《Python核心技術與實戰》專欄