什么是装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
本质上,decorator就是一个返回函数的高阶函数。
例子
定义一个打印日志的装饰器
def log(func):
def wrapper(*args, **wk):
print("call %s" %func.__name__ )
return func(*args, **wk)
return wrapper
log是一个装饰器,接收一个函数作为输入参数,并返回一个函数。
@log
def now():
print("2019-03-07")
now()
运行结果为:
call now:
2019-03-07
把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
由于log是一个装饰器,返回一个函数(对象),所以原来的now()函数仍然存在,只是现在同名的now变量指向了新函数,于是调用now()将执行新函数,即在log()中返回的wrapper()函数
所以:
f = log(now)
print(f == now())
False
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text):
def decorate(func):
def wrapper(*args, **wk):
print("%s %s:" %(text, func.__name___))
return func(*args, **wk)
return wrapper
return decorate
@log('execute')
def now():
print(‘2019-03-07')
now()
输出结果为:
execute now:
2019-03-07
此时,now.__name__变成了wrapper,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# 或者带参数的log
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
练习
- 请编写一个decorator,能在函数调用的前后打印出’begin call’和’end call’的日志
def log(func):
def wrapper(*args, **kw):
print("begin %s" %func.__name__)
f = func(*args, **kw)
print("end %s" %func.__name__)
return f
return wrapper
@log()
def now():
print('2019-03-07')
now()
- 写出一个@log的decorator,使它既支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
# 设置默认参数为空
def log(text = ''):
def decorate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("%s %s" %(text, func.__name__))
return func(*args, **kwargs)
return wrapper
return decorate
@log()
def now():
print(time.time())
@log('execute')
def now2():
print(time.time())
now()
now2()