python 裝飾器 Decorator
阿新 • • 發佈:2017-12-17
log 運行 效果 args 增加 原來 函數 app per
一、裝飾器定義
在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。本質上,Decorator就是一個返回函數的高階函數。
1 >>> def log(func): 2 ... def wrapper(*args, **kw): 3 ... print(‘call %s:‘ % func.__name__) 4 ... return func(*args, **kw) 5 ... return wrapper 6 ... 7 >>> @log 8 ... def now():9 ... print(‘2017-12-16‘) 10 ... 11 >>> now() 12 call now: 13 2017-12-16 14 >>>
觀察上面的log
,因為它是一個decorator,所以接受一個函數作為參數,並返回一個函數。要借助Python的@語法,把decorator置於函數的定義處。
把@log
放到now()
函數的定義處,相當於執行了語句:
1 >>>now = log(now)
二、帶傳參的裝飾器
1 >>> def log(text): 2 ... def decorator(func):3 ... def wrapper(*args, **kw): 4 ... print(‘%s %s:‘ % (text, func.__name__)) 5 ... return func(*args, **kw) 6 ... return wrapper 7 ... return decorator 8 ... 9 >>> @log(‘execute‘) 10 ... def now(): 11 ... print(‘2017-12-16‘) 12 ... 13 >>> now()14 execute now: 15 2017-12-16
和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
1 >>> now = log(‘execute‘)(now)
三、functools.wraps
以上兩種decorator的定義都沒有問題,但還差最後一步。因為我們講了函數也是對象,它有__name__
等屬性,但你去看經過decorator裝飾之後的函數,它們的__name__
已經從原來的‘now‘
變成了‘wrapper‘
:
1 >>> now.__name__ 2 ‘wrapper‘
因為返回的那個wrapper()
函數名字就是‘wrapper‘
,所以,需要把原始函數的__name__
等屬性復制到wrapper()
函數中,否則,有些依賴函數簽名的代碼執行就會出錯。
不需要編寫wrapper.__name__ = func.__name__
這樣的代碼,Python內置的functools.wraps
就是幹這個事的,所以,一個完整的decorator的寫法如下:
1 import functools 2 3 def log(func): 4 @functools.wraps(func) 5 def wrapper(*args, **kw): 6 print(‘call %s():‘ % func.__name__) 7 return func(*args, **kw) 8 return wrapper
1 import functools 2 3 def log(text): 4 def decorator(func): 5 @functools.wraps(func) 6 def wrapper(*args, **kw): 7 print(‘%s %s():‘ % (text, func.__name__)) 8 return func(*args, **kw) 9 return wrapper 10 return decorator
python 裝飾器 Decorator