装饰器就是在函数外再套一层函数,用来通用地增强函数功能,例如打印函数的日志信息。
例如,要定义一个能打印日志的decorator,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
使用时,只需在函数前一行加上 @log
@log
def now():
print('2015-3-25')
>>> now()
call now():
2015-3-25
等价于执行
now = log(now)
如果装饰器本身要传入参数
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
这个3层嵌套的decorator用法如下:
@log('execute')
def now():
print('2015-3-25')
执行结果如下:
>>> now()
execute now():
2015-3-25
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
>>> now = log('execute')(now)
还原原函数的属性
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
练习
请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
def metric(fn):
def wrapper(*args, **kw):
start = time.time()
a=fn(*args, **kw)
end = time.time()
print('%s executed in %s ms' % (fn.__name__,end-start))
return a
return wrapper
输出为:
fast executed in 0.0012867450714111328 ms
slow executed in 0.12357020378112793 ms
请编写一个decorator,能在函数调用的前后打印出'begin call'
和'end call'
的日志。
再思考一下能否写出一个@log
的decorator,使它既支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
代码:
import functools
def log(unknow_arg):
if type(unknow_arg)==str:
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (unknow_arg, func.__name__))
print('begin call')
a=func(*args, **kw)
print('end call')
return a
return wrapper
return decorator
else:
@functools.wraps(unknow_arg)
def wrapper(*args, **kw):
print('begin call')
a=unknow_arg(*args, **kw)
print('end call')
return a
return wrapper
输出分别为:
begin call
end call
execute f():
begin call
end call
用 class 实现装饰器
class Logger():
def __init__(self,func):
self._func=func
def __call__(self):
print(f'{self._func.__name__} is running...')
self._func()
@Logger
def test_fun():
print('Test!')
>>>test_fun()
test_fun is running...
Test!