[轉]理解Python裝飾器
阿新 • • 發佈:2018-12-20
作者:xlzd
連結:http://www.zhihu.com/question/26930016/answer/81263287
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。簡單來講,可以不嚴謹地把Python的裝飾器看做一個包裝函式的函式。
連結:http://www.zhihu.com/question/26930016/answer/81263287
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。簡單來講,可以不嚴謹地把Python的裝飾器看做一個包裝函式的函式。
比如,有一個函式:
def func():
print 'func() run.'
if '__main__' == __name__:
func()
執行後將輸出:
func() run.
現在需要在函式執行前後列印一條日誌, 但是又不希望或者沒有許可權修改函式內部的結構, 就可以用到裝飾器(decorator):
def log(function ):
def wrapper(*args, **kwargs):
print 'before function [%s()] run.' % function.__name__
rst = function(*args, **kwargs)
print 'after function [%s()] run.' % function.__name__
return rst
return wrapper
@log
def func():
print 'func() run.'
if '__main__' == __name__:
func()
對於原來的函式”func()”並沒有做修改,而是給其使用了裝飾器log,執行後的輸出為:
before function [func()] run.
func() run.
after function [func()] run.
把”@log”放到func()函式定義的地方,相當於執行了如下語句:
func = log(func)
因為log()返回了一個函式, 所以原本的func指向了log()返回的函式wrapper。wrapper的引數列表為(*args, **kwargs), 所以其可以接受所有的引數呼叫, 在wrapper中,先列印了一行
‘before function [%s()] run.’ % function.__name__
(在Python中函式也是物件,函式的__name__是它的名字),然後執行了原來的函式並記錄了返回值,在輸出
‘after function [%s()] run.’ % function.__name__
後返回了函式的執行結果。
如果decorator本身需要傳入引數,那就需要編寫一個返回decorator的decorator。比如在Flask中:
@app.route('/')
def index():
return 'hello, world!'
實現如下:
import functools
def log(text=''):
def decorator(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
print 'before function [%s()] run, text: [%s].' % (function.__name__, text)
rst = function(*args, **kwargs)
print 'after function [%s()] run, text: [%s].' % (function.__name__, text)
return rst
return wrapper
return decorator
@log('log text')
def func():
print 'func() run.'
if '__main__' == __name__:
func()
輸出如下:
before function [func()] run, text: [log text].
func() run.
after function [func()] run, text: [log text].
最後腦洞小開一下, 有沒有辦法實現既支援不帶引數(如log), 又支援帶引數(如log(‘text’))的decorator嗎?
import functools
def log(argument):
if not callable(argument):
def decorator(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
print 'before function [%s()] run, text: [%s].' % (function.__name__, text)
rst = function(*args, **kwargs)
print 'after function [%s()] run, text: [%s].' % (function.__name__, text)
return rst
return wrapper
return decorator
def wrapper(*args, **kwargs):
print 'before function [%s()] run.' % function.__name__
rst = function(*args, **kwargs)
print 'after function [%s()] run.' % function.__name__
return rst
return wrapper
如上~~~