理解Python闭包¶
Info
作者:Vincent,发布于2021-09-21,阅读时间:约3分钟,微信公众号文章链接:
1 前言¶
在使用Python的日常工作中,你或许碰到过类似于这样的代码:
def make_counter():
# 外层闭包函数
count = 0
def counter():
# 嵌套函数
nonlocal count
count += 1
return count
return counter
一个函数里还有一个函数,并且外层函数的返回值是内层的函数,为什么要这样定义函数呢?这样有什么好处,这篇文章我们来揭开它神秘的面纱 - 闭包(Closure)。
2 闭包的要点¶
闭包指延伸了作用域的函数,它会引用一个不在函数定义中的非全局变量(如上述例子中的count),nonlocal的加入能把变量标记为自由变量(Python 3才加入nonlocal关键字),让内层嵌套函数可以修改作用域外的不可变变量。
调用make_counter时,返回一个counter函数对象,每次调用counter时,它会更新count,示例如下:
# 运行闭包函数
counter = make_counter()
print(counter())
print(counter())
输出为:
1
2
在这个例子中,有一点需要展开说的是count的历史值的存储位置,count是make_counter函数的局部变量,初始化的时候count的值为0,但调用counter的时候,make_counter函数已经返回了,本地作用域理应不复存在。
在counter函数中,count是自由变量,counter函数实现了对变量count的绑定。可以通过Python中的__code__属性(表示编译后的函数定义体)查看保存的局部变量和自由变量的名称,如下所示:
# 查看自由变量
counter.__code__.co_freevars
输出为:
('count',)
count的绑定在返回的counter函数的__closure__属性里,其中__closure__的各个元素对应于counter.__code__.co_freevars
中的一个名称。这些元素是cell对象,可以通过cell_contents属性访问其存储的值,示例如下:
counter.__closure__[0].cell_contents
输出为:
2
闭包能够非常简洁、直观地解决轻量级的问题,如果上述功能用类
来实现的话,会长成这样:
# 用类定义一个计数器,从0开始计数
class Counter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
counter = Counter()
print(counter())
print(counter())
输出为:
1
2
3 总结¶
闭包是一种函数,它保留了定义函数时存在的自由变量的绑定,因此,即便是函数返回后作用域不存在了,那些绑定仍然能够被使用。闭包可以很容易地实现一些简单的类的功能,同时基于此还有很多Python的魔法能够实现,比如装饰器,待下回分解!
希望这次的分享对你有帮助,欢迎在评论区讨论!