一、闭包函数
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
import sys
debug_log = sys.stderr
def trace(func):
if debug_log:
def callf(*args, **kwargs):
"""A wrapper function."""
debug_log.write('Calling function: {}\n'.format(func.__name__))
res = func(*args, **kwargs)
debug_log.write('Return value: {}\n'.format(res))
return res
return callf
else:
return func
注:
1、如果想修改外部变量的值,可以是使用`nonlocal`进行声明
2、由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
二、何谓“装饰器”?
“装饰器是一个函数,其主要用途是包装另一个函数或类。这种包装的首要目的是透明地修改或增强被包装对象的行为。” --《A Byte of Python》
在上面定义好的闭包函数使用@函数语法糖,便可将trace声明为装饰器函数:
@trace
def square(x):
"""Calculate the square of the given number."""
return x * x
if __name__ == '__main__':
print(square(9))
以上函数完全等价于:@trace 等价于 trace(square)。如果不用装饰器语法,我们也可以这样写:
def _square(x):
return x * x
square = trace(_square)
在闭包函数中func(*args, **kwargs)之前,我们可以加入任何的我们想要加入的新增,从而对func本身逻辑并没有影响。
嗯…以上是便是不带参的装饰器的基本的用法了,下面再简单说明一下带参装饰器:
def trace(log_level):
def impl_f(func):
print(log_level, 'Implementing function: "{}"'.format(func.__name__))
return func
return impl_f
@trace('[INFO]')
def print_msg(msg):
print(msg)
[INFO] Implementing function: "print_msg"
>>> @trace('[DEBUG]')
def assert_(expr): assert expr
[DEBUG] Implementing function: "assert_"
>>> print_msg('Hello, world!')
Hello, world!
带参数的trace装饰器函数等价于:trace("[INFO]")(print_msg)
嗯…装饰器拥有如此强大的功能,是否感到一点兴奋!假设我们把如上代码提供给其他程序员使用,他可能会想看一下square函数的帮助文档:
>>> from decorator_wraps_test import square
>>> help(square) # print(square.__doc__)
Help on function callf in module decorator_wraps_test:
callf(*args, **kwargs)
A wrapper function
看到这样的结果,使用decorator_wraps_test.py模块的程序员一定会感到困惑。他可能会带着疑问敲入如下代码:
print(square.__name__)
callf
这下,他可能会想看一看decorator_wraps_test.py的源码,找一找问题究竟出现在了哪里。我们知道,Python中所有对象都是“第 一类”的。比如,函数(对象),我们可以把它当作普通的数据对待:我们可以把它存储到容器中,或者作为另一个函数的返回值。这样,把trace返回的callf存储到square时,我们得到的不仅仅是callf函数执行语句,还有其上下文环境:
>>> print('debug_log' in square.__globals__)
True
>>> print('sys' in square.__globals__)
True
因此,使用装饰器修饰过的函数square,实际上是一个trace函数返回的“闭包”对象callf,这就揭示了上面help(square)以及print(square.name)的输出结果了。
那么,怎样才能在使用装饰器的基础上,还能让help(square)及print(square.name)得到我们期待的结果呢?这就是functools模块的wraps装饰器的作用了。
三、functools之wraps
先看看效果:
import functools
import sys
debug_log = sys.stderr
def trace(func):
if debug_log:
@functools.wraps(func)
def callf(*args, **kwargs):
"""A wrapper function."""
debug_log.write('Calling function: {}\n'.format(func.__name__))
res = func(*args, **kwargs)
debug_log.write('Return value: {}\n'.format(res))
return res
return callf
else:
return func
@trace
def square(x):
"""Calculate the square of the given number."""
return x * x
if __name__ == '__main__':
print(square(9))
print(square.__doc__)
print(square.__name__)
执行以上,输出:
Calling function: square
81
Return value: 81
Calculate the square of the given number.
square
至此,完美撒花!我们使用了一个带参数的wraps装饰器“装饰”了嵌套函数callf,得到了预期的效果。
参考:https://www.cnblogs.com/myd7349/p/how_to_use_wraps_of_functools.html