闭包的内存布局核心是自由变量通过cell对象建立引用绑定,而非复制值;__closure__元组存储cell,每个cell持指向实际对象的指针,load_deref指令解引用访问。

闭包的内存布局,核心在于「自由变量如何被保存」——不是简单地复制一份值,而是通过 cell 对象 建立引用绑定,让内部函数能持续访问外部函数已销毁作用域中的变量。
自由变量被封装进 cell 对象
Python 中,当内部函数引用了外层函数的局部变量(即自由变量),解释器不会让该变量随外层函数栈帧销毁而消失。它会把变量包装成一个 cell 对象,存入内部函数的 __closure__ 属性中。
-
__closure__是一个元组,每个元素都是一个cell;为空则为None -
cell不是普通容器,它持有一个指向实际对象的指针(类似弱引用但更牢固) - 修改自由变量(如用
nonlocal)时,改的是cell所指向的对象内容,而非替换cell本身
__closure__ 是闭包存在的铁证
判断一个函数是不是闭包,不看它有没有被返回,只看它的 __closure__ 是否非空且含 cell:
- 有
__closure__且每个元素是cell→ 确实捕获了自由变量 → 是闭包 - 即使没被返回(比如赋给全局变量),只要满足嵌套 + 引用自由变量,
__closure__就存在 -
cell.contents可读取当前绑定的值(调试可用,生产中一般不直接操作)
内存中实际布局示意
假设外层函数定义 msg = "hello",内层函数打印 msg 并被返回:
- 外层函数执行完 → 栈帧销毁,
msg名字消失,但对象"hello"未被回收 - 解释器创建
cell,其内部指针指向字符串对象"hello" - 该
cell被挂到内层函数的__closure__元组里 - 后续调用内层函数时,从
__closure__中取出cell,再解引用拿到值
为什么不能直接访问自由变量?
自由变量在字节码层面通过 LOAD_DEREF 指令读取(区别于 LOAD_FAST 或 LOAD_GLOBAL),它明确告诉解释器:去 __closure__ 里找对应序号的 cell,再取内容。这个机制隔离了作用域,也保证了变量生命周期由闭包持有者决定。











