一 闭包:函数作参数,返回值,嵌套函数
(https://www.runoob.com/w3cnote/python-func-decorators.html)
装饰器
import functools
定义一个装饰器log
def log(func): # 函数做参数
@functools.wraps(func)
def wrapper(*args, **kwargs): # 定义嵌套函数
print('call %s():' % func.__name__)
print('args = {}'.format(*args))
return func(*args, **kwargs)
return wrapper #嵌套的函数做返回值
@log
def test(p):
print(test.__name__ + " param: " + p)
调用
test("I'm a param")
运行结果
call test():
args = I'm a param
test param: I'm a param
实际上是test做log的参数,即@语法将函数test传入装饰器函数log,
先调用log(test),返回函数wrapper
再显示执行wrapper("I'm a param"),执行两个print,返回并执行函数test("I'm a param")
注意哈wrapper里面是return func(*args, **kwargs)不是return func,
因此不同于log里面return wrapper;log返回后wrapper还需要显示调用wrapper("I'm a param"),
而test不需要了直接执行test函数
因此上述调用方式等价于下面这种写法
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('call %s():' % func.__name__)
print('args = {}'.format(*args))
return func(*args, **kwargs)
return wrapper
def test(p):
print(test.__name__ + " param: " + p)
wrapper = log(test)
wrapper("I'm a param")
因此装饰器在使用时,用了@语法,让人有些困扰。其实,装饰器只是个方法
@语法只是将函数传入装饰器函数,并无神奇之处。
值得注意的是@functools.wraps(func),这是python提供的装饰器。
它能把原函数test的元信息test.name拷贝到装饰器log里面的 func 函数func.name中。函数的元信息包括docstring、name、参数列表等等。
可以尝试去除@functools.wraps(func),会发现test.__name__的输出变成了wrapper。