赞
踩
闭包作为python高级特性中的一个,初学总觉其披着一层神秘的面纱,这里我们来一起揭开这层面纱吧。那什么是闭包呢?
闭包,又称闭包函数,和普通的嵌套函数类似,闭包中包含外部函数和内部函数,不同之处是闭包中外部函数返回的不是一个值,而是内部函数,即闭包对象;通常,该返回的函数会被赋值给一个变量,继而被调用执行。
比如,已知三条直线上某一个点的横坐标x,求其纵坐标y。如果不使用闭包,实现如下:
- def line1(x):
- return 2 * x + 1
-
- def line2(x):
- return 3 * x + 2
-
- def line3(x):
- return 5 * x + 3
-
- if __name__ == '__main__':
- print(line1(2))
- print(line2(2))
- print(line3(2))
使用闭包,实现如下
- # 闭包
- def line_closure(a, b):
- def line(x):
- return a * x + b
- return line
-
- if __name__ == '__main__':
- line4 = line_closure(2, 1) # 返回闭包对象,赋值给line4
- print(line4(2)) # 此处可调用闭包里的内部函数
- line5 = line_closure(3, 2)
- print(line5(2))
- line6 = line_closure(5, 3)
- print(line6(2))
5 8 13 5 8 13
以上两种方法结果一样,在只有三条直线的情况下,代码量区别不大,而当有几百条直线呢,我们是不是还要定义几百个函数呢,此时用闭包就会使代码变得简单美观,提高了代码的复用率。
给不同项目记录日志
- import logging
-
- def log_header_closure(project_name):
- logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(name)s] %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
- logger = logging.getLogger(project_name)
-
- def _logging(message, level):
- if level == 'debug':
- logger.debug(message)
- elif level == 'warning':
- logger.warning(message)
- elif level == 'error':
- logger.error(message)
- return _logging
-
- if __name__ == '__main__':
- project_1_logger = log_header_closure('project_1')
- project_1_logger('this is a warning info', 'warning')
- project_1_logger('this is a error info', 'error')
-
- project_2_logger = log_header_closure('project_2')
- project_2_logger('this is a warning info', 'warning')
- project_2_logger('this is a error info', 'error')
-

2021-10-27 14:21:32 [project_1] WARNING this is a warning info
2021-10-27 14:21:32 [project_1] ERROR this is a error info
2021-10-27 14:21:32 [project_2] WARNING this is a warning info
2021-10-27 14:21:32 [project_2] ERROR this is a error info
其实,装饰器就是闭包,只不过装饰器的外部函数参数是一个函数,内部函数对该函数进行加工修饰最后返回。
- def add(x, y):
- return x + y
-
- # 装饰器,实际上就是一个闭包
- def wrap_outer(func):
- def wrap_inner(x, y):
- print(time.asctime())
- print('run start')
- result = func(x, y)
- print('run finished')
- print(time.asctime())
- return result
- return wrap_inner
-
- if __name__ == '__main__':
- wrapper = wrap_outer(add)
- print(wrapper(2, 3))

Wed Oct 27 11:59:19 2021
run start
run finished
Wed Oct 27 11:59:19 2021
5
接着,我们用python装饰器的“语法糖”来重新实现一下,让代码看起来更优雅些:
- from functools import wraps
-
- def wrap_outer(func):
- @wraps(func)
- def wrap_inner(*args, **kwargs):
- print(time.asctime())
- print('run start')
- result = func(*args, **kwargs)
- print('run finished')
- print(time.asctime())
- return result
- return wrap_inner
-
- @wrap_outer
- def add(x, y):
- return x + y
-
- if __name__ == '__main__':
- print(add(2, 3))

Wed Oct 27 13:39:55 2021
run start
run finished
Wed Oct 27 13:39:55 2021
5
可以看出,执行结果跟上面闭包实现一模一样。
闭包比普通函数多了一个'__closure__'属性,用来记录引用的外部函数的自由变量的地址。当闭包被调用时,会从该地址获取到该自由变量,并完成整体的函数调用。
如上面的日志例子:
- project_1_logger = log_header_closure('project_1')
- print('111', project_1_logger.__closure__)
(<cell at 0x03AF7370: Logger object at 0x0E56D1B0>,)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。