闭包变量实际存储在cell对象中,由外部函数栈帧中的局部变量被内部函数引用时创建,并通过__closure__元组持有;cell维持对原始内存位置的间接访问,使变量生命周期延长至闭包存在期间。

因为Python在编译函数时就识别出自由变量,并用cell对象封装它们,使内部函数通过__closure__持有对这些变量的引用——不是拷贝值,而是维持对原始栈帧中变量的间接访问通道。
闭包变量实际存储在哪?
外部函数执行时,其局部变量本该随栈帧销毁,但一旦被内部函数引用,CPython会把该变量包装成一个cell对象,放入内部函数的__closure__元组。这个cell像一个“指针容器”,始终指向变量所在内存位置(即使外部函数已返回)。
-
__closure__为None→ 不是闭包;否则是tuple,每个元素是cell -
cell.cell_contents可读取当前值,修改它会影响所有共享该cell的闭包 - 自由变量名记录在
__code__.co_freevars里,是编译期静态确定的
为什么循环中多个闭包常共享同一个变量?
这是后期绑定(late binding)导致的:闭包捕获的是变量名的引用,而非定义时的值。循环结束时,控制变量(如i)只保留最终值,所有闭包调用时都去读这个最终值。
- 错误写法:
[lambda x: x * i for i in range(3)]→ 三个闭包都读i == 2 - 正确冻结值:
[lambda x, i=i: x * i for i in range(3)],利用默认参数在定义时求值 - 或改用函数工厂:
def make_mul(n): return lambda x: x * n,每次调用生成独立cell
修改闭包中的外部变量为什么报错?
直接赋值会触发UnboundLocalError,因为Python默认把赋值目标当作局部变量。必须显式声明nonlocal才能写入。
- 读取自由变量:无需声明,自动向上查找作用域
- 写入自由变量:必须用
nonlocal varname,否则解释器认为你在创建同名局部变量 - 若外部变量是可变对象(如
list),可原地修改(.append()),无需nonlocal
真正容易被忽略的是cell的生命周期:只要还有闭包引用它,对应变量就不会被垃圾回收——哪怕外部函数早已返回。大量长期存活的闭包可能隐式延长对象寿命,尤其当捕获了大对象或资源句柄时。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











