裝飾器(Decorator)
阿新 • • 發佈:2019-02-09
1. 函式也是物件
在python中,函式也是物件,它有自己的方法,它可以傳遞下去。
函式傳遞
>>> def brown():
print 'brown'
>>> new_brown = brown # 函式物件傳遞
>>> new_brown()
brown
2. 什麼是裝飾器
裝飾器實際上是函式,它以函式物件為引數,可以在它所裝飾的函式的前或後新增一些其它的操作。你可以在執行(被裝飾的)函式之前做一些預準備工作,也可以在執行(被裝飾的)函式之後做一些清理工作,比如事後分析。
通常,當你wrap一個函式時,你應該在wrapper裡呼叫它。你可以在wrapper裡面隨時呼叫它。
>>> from functools import wraps
>>> def deco(func): # 定義裝飾器
@wraps(func)
def wrapper(*args, **kw_args):
print 'Do something prepared!'
func(*args, **kw_args)
print 'Do something cleanup!'
return wrapper
>>> @deco # 使用裝飾器
def hello():
print 'Hello Brown!'
>>> hello()
Do something prepared!
Hello Brown!
Do something cleanup!
注意:
使用裝飾器時使用了下述語法:
@deco def foo(): # To do something
其實等價於下述語法:
def foo(): # To do something foo = deco(foo)
定義裝飾器時,在
wrapper()
函式上使用了functools.wraps
hello.__name__
你會發現
hello
的函式名不再是hello
了,這意味著元資訊丟了。
3. 裝飾器的作用
裝飾器可以用來:
- 引入日誌
- 增加計時邏輯來檢測效能
- 給函式加入事務的能力
4. 帶引數的裝飾器
注意,上面所講的裝飾器是沒有引數的,e.g.@deco
;如果我們需要給裝飾器傳遞引數,我們需要:定義一個返回(以被裝飾函式作為引數的)裝飾器的函式。有點拗口,簡單地說,就是在我們之前的裝飾器函式外再包裹一層函式,這個函式返回我們定義的無引數裝飾器。
>>> from functools import wraps
>>> def deco_param(param):
def deco(func):
@wraps(func)
def wrapper(*args, **kw_args):
print 'Do something prepared!'
print 'Param is %s!' % param
func(*args, **kw_args)
print 'Do something cleanup!'
return wrapper
return deco
>>> @deco_param('BrownWong')
def hello():
print 'Hello Brown!'
>>> hello()
Do something prepared!
Param is BrownWong!
Hello Brown!
Do something cleanup!
注意:
@deco_param(param)
def hello():
# To do something
等價於
def hello():
# To do something
hello = deco_param(param)(hello)
5. 堆積裝飾器
裝飾器可以堆積起來,如下
@deco1
@deco2
def func():
# To do something
它等價於:
func = deco1(deco2(func))
6. 使用裝飾器為函式打日誌
我們看一個例子,例子會寫一個用來打日誌的裝飾器,裝飾器可以列印被裝飾函式的位置、函式名、輸入引數、函式消耗時間、函式返回值。
函式版本:
from functools import wraps
# 定義裝飾器函式
def log_deco(logger):
def wrapper1(func):
@wraps(func)
def wrapper2(*args, **kw_args):
init_time = time.time()
result = func(*args, **kw_args)
spend_time = time.time() - init_time
logger.info('{}: {}(), params: {}, {}, result: {}, spend time: {}'.format(func.__module__, func.func_name, args, kw_args, result, spend_time))
return result
return wrapper2
return wrapper1
# 使用裝飾器函式
@log_deco(logger)
def my_func(a, b, c=1):
time.sleep(1)
return a + b + c
if __name__ == '__main__':
my_func(1, 2, c=3)
類版本:
from functools import wraps
# 定義裝飾器類
class LogDeco(object):
def __init__(self, logger):
self.logger = logger
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kw_args):
init_time = time.time()
result = func(*args, **kw_args)
spend_time = time.time() - init_time
self.logger.info('{}: {}(), params: {}, {}, result: {}, spend time: {}s'.format(\
func.__module__, func.func_name, args, kw_args, result, spend_time))
return result
return wrapper
# 使用裝飾器
@LogDeco(logger)
def my_func(a, b, c=1):
time.sleep(1)
return a + b + c
if __name__ == '__main__':
my_func(1, 2, c=3)
Ref