以下面的廖雪峰老师python教程里装饰器小节留下的练习为例:
写出一个 @log
的decorator,使它既支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
参考写法:
import functools
def log(param = None):
def decorator(fn):
@functools.wraps(fn)
def wrap(*args, **kw):
print('begin call')
r = fn(*args, **kw)
print('end call')
return r
return wrap
return decorator if isinstance(param,str) else decorator(param)
# return decorator
@log
def f():
print('call f')
@log('execute')
def f2():
print('call f2')
f()
f2()
运行结果:
begin call
call f
end call
begin call
call f2
end call
解析:
第一个无参的 @log 的例子,当我们调用 f() 时,调用的其实是 log(f) ,则传给 log 的 param 参数就是函数 f,而 decorator(fn) 函数才是我们真正定义的装饰器,它需要接收一个参数(我们的目标函数),而此时实际执行的确实 log(f),目标函数已经作为参数被 param 接收了,所以我们在 log 函数中要返回 decorator(param),若直接返回 decorator,则没有传递目标函数,报缺少参数的错误。
第二个有参的 @log 的例子,当我们调用 f() 时,调用的其实是 log('execute')(f) ,传给 log 的 param 参数就是 'execute',然后又把目标函数 f 传给返回的 decorator 函数,因此我们在 log 函数最终只要返回 decorator 即可。
也可以这么写:
import functools
def log(param = None):
def decorator(fn):
@functools.wraps(fn)
def wrap(*args, **kw):
print('begin call')
r = fn(*args, **kw)
print('end call')
return r
return wrap
# return decorator if isinstance(param,str) else decorator(param)
return decorator
@log()
def f():
print('call f')
@log('execute')
def f2():
print('call f2')
f()
f2()
即无论 log 函数传不传参数,在使用 @log的时候都把 () 打上,即若不传参数,则传空值给 param,这样目标函数 fn 就能别装饰器包装到,最后 log 可以统一返回装饰器。