Python闭包必须满足三条件:嵌套结构、内部函数引用外部非全局变量、外部函数返回内部函数对象;否则易现UnboundLocalError、循环变量共享、内存泄漏等问题。

闭包必须满足的三个硬性条件
Python 中一个函数要成为闭包,必须同时满足:有嵌套结构、内部函数引用了外部函数的非全局变量、外部函数返回了内部函数对象(不是调用结果)。缺一不可。
常见误判是把“有嵌套”等同于“是闭包”,比如 outer() 里定义 inner() 但没返回它,或者返回的是 inner()(带括号)——这只会执行并返回 None,不是闭包。
-
def outer(): x = 10; def inner(): return x; return inner✅ 是闭包(返回函数对象) -
def outer(): x = 10; def inner(): return x; return inner()❌ 不是闭包(返回的是值) -
def outer(): x = 10; def inner(): return x; inner()❌ 不是闭包(没返回,也没被返回)
修改自由变量时出现 UnboundLocalError
当内部函数尝试给自由变量赋值(如 x += 1),Python 会把它当作局部变量,但读取时又发现未初始化,直接报 UnboundLocalError: local variable 'x' referenced before assignment。
根本原因是 Python 在编译阶段就根据赋值语句判定作用域,不看运行时逻辑。即使 x 在外层已定义,只要内部有写操作,它就被锁定为局部变量。
立即学习“Python免费学习笔记(深入)”;
- 解决办法只有两个:
nonlocal x(声明修改外层变量)或改用可变容器(如list[0]、dict['key'])间接修改 - 注意
global不起作用——自由变量不属于全局作用域 -
+=、-=、*=等增强赋值都会触发该错误,不只是=
循环中创建多个闭包却共享同一变量
这是最经典也最容易踩的坑:在 for 循环里定义闭包并捕获循环变量,所有闭包最终都记住循环结束后的最后一个值。
原因在于闭包捕获的是变量名的引用,而不是当前迭代的值;而循环变量在每次迭代中复用同一个名字,循环结束后只保留终值。
- 典型错误写法:
funcs = []; for i in range(3): funcs.append(lambda: i)→ 全部返回2 - 修复方式一:用默认参数固化当前值:
lambda i=i: i - 修复方式二:用
functools.partial绑定参数:partial(lambda x: x, i) - 不推荐用
time.sleep或其他延迟来“绕过”——问题本质是作用域绑定时机,不是执行顺序
闭包导致对象无法被垃圾回收
闭包会隐式持有对外层作用域对象的引用,如果外层变量引用了大型对象(如大列表、DataFrame、网络连接),而闭包长期存活(比如被注册为回调、存在全局字典中),这些对象就无法被 GC 回收。
尤其要注意类方法中返回闭包、或闭包里引用了 self 的场景,容易形成循环引用。
- 检查方式:用
obj.__closure__查看闭包引用了哪些 cell 对象,再通过cell.cell_contents看实际内容 - 避免长期持有时,显式将不需要的变量设为
None,或改用普通函数+显式传参 -
weakref一般不适用——闭包引用是强引用,且不能对局部变量弱引用










