装饰器
装饰器就是返回函数的实际运用,装饰器接受一个原函数作为参数,返回值是一个现函数,调用装饰器就可以在原函数调用前后进行操作,而不改变原函数。
def now(): print('2015-3-25') def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper log(now)()
以上述代码为例,now为原函数,log为装饰器,log接受函数func作为输入参数,返回wrapper函数作为返回值,而wrapper函数在func函数调用前打印日志,wrapper函数可以接受任意参数的调用,代表原始函数的参数可以是任意形式的。
最后的log(now)先返回wrapper,然后log(now)()就调用了wrapper(),而wrapper()首先打印了日志,然后返回了func(),而func()就是now()。
也可以使用@log对now重新定义,这样now就是一个装饰器
@log def now(): print('2015-3-25')
这样就相当于运行了now=log(now),因此直接运行now()就达到了上面的效果,但是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
调用时可以log('想要打印的日志')(now)(),也可以在原函数定义前加@log('想要打印的日志')
如果使用了装饰器重新定义函数,那么它的__name__等属性就变为了wrapper,因此需要在装饰器定义前加上@functools.wraps(func)
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
作业有个思考题解法很有意思
import functools def log(args): if isinstance(args,str): def dec(func): @functools.wraps(func) def wrapper(*a,**kw): print('begin func',args) func(*a,**kw) print('end func') return wrapper return dec else: @functools.wraps(args) def wrapper(*a, **kw): print('begin func', args) args(*a, **kw) print('end func') return wrapper @log('execute') def f(): print('I am here') @log def f(): print('I am here') f()
@log
def f():
pass
等价于 f=log(f)
@log('execute')
def f():
pass
等价于f=log('execute')(f)
解法中根据log()括号中的参数类型是否为字符串来判断,如果参数为字符串,那么属于三层嵌套的装饰器,如果参数不是字符串,那么就将args作为被调用的函数名参数,即两层嵌套。
课外阅读:
http://www.cnblogs.com/myd7349/p/how_to_use_wraps_of_functools.html