在 Python 3 中,闭包(Closure) 是指在一个嵌套函数中,内部函数引用了外部函数的自由变量(即外部函数作用域中的变量),并且外部函数返回了内部函数本身。这样,即使外部函数执行完毕,内部函数依然可以记住并访问那些被引用的外部变量,形成了一种“闭包”环境。
闭包的三个必要条件
- 必须有一个嵌套函数(函数内部定义另一个函数)。
- 内部函数必须引用外部函数的变量(非全局变量)。
- 外部函数必须返回内部函数(或者把内部函数传给其他对象)。
最简单的闭包示例
def outer(x):def inner(y):return x + yreturn inneradd_5 = outer(5) # 返回 inner 函数,并记住了 x=5
print(add_5(3)) # 输出 8
print(add_5(10)) # 输出 15
在这里,inner 引用了外部函数 outer 的局部变量 x。即使 outer 执行完毕,add_5 仍然持有 x=5 的值,这就是闭包。
如何查看闭包中捕获的变量
可以通过函数的 __closure__ 属性查看:
def outer(x):def inner(y):return x + yreturn innerf = outer(10)
print(f.__closure__) # (<cell at 0x...: int object at 0x...>,)
print(f.__closure__[0].cell_contents) # 10
闭包与 nonlocal 关键字
如果内部函数需要修改外部函数的变量(而不是仅仅读取),必须使用 nonlocal 声明,否则 Python 会认为你在内部函数中新建了一个局部变量。
def counter():count = 0def increment():nonlocal count # 声明 count 不是局部变量count += 1return countreturn incrementc = counter()
print(c()) # 1
print(c()) # 2
如果没有 nonlocal,count += 1 会抛出 UnboundLocalError。
闭包的常见应用场景
- 装饰器(Decorator):闭包是实现装饰器的核心机制,装饰器接受函数,在内部包装并返回新函数。
- 工厂函数:生成带有特定配置的函数(如上面的
outer)。 - 回调函数:在事件驱动或异步编程中,携带额外状态。
- 函数式编程:实现柯里化(Currying)、部分应用等。
注意事项
- 变量生命周期:闭包会延长外部变量的生命周期,它们不会在外部函数返回后立即释放,而是随闭包函数一起存在。如果大量使用闭包且不注意,可能导致内存泄漏。
- 循环中的闭包陷阱:在循环中创建闭包时,如果内部函数引用了循环变量,往往得到意外的结果(所有函数共享同一个变量)。解决方案是使用默认参数或
functools.partial。
# 错误示例
funcs = []
for i in range(3):funcs.append(lambda: i) # 所有 lambda 都引用同一个 i
print(funcs[0]()) # 2
print(funcs[1]()) # 2
print(funcs[2]()) # 2# 正确做法:用默认参数捕获当前值
funcs = []
for i in range(3):funcs.append(lambda i=i: i) # 默认参数在定义时求值
print(funcs[0]()) # 0
print(funcs[1]()) # 1
print(funcs[2]()) # 2
- 性能:闭包比普通函数调用稍慢,因为涉及自由变量的访问(通过
LOAD_DEREF指令),但在大多数场景下可忽略。
与 Python 2 的区别
- Python 2 中,闭包只能读取外部变量,不能修改(没有
nonlocal)。Python 3 引入了nonlocal,使得修改外层非全局变量成为可能。 - Python 3 对闭包的实现更加清晰,
__closure__属性统一。
总结
闭包是 Python 函数式编程的重要基石,理解它有助于掌握装饰器、高阶函数等高级特性。关键在于嵌套函数、引用自由变量和返回函数,并注意使用 nonlocal 来修改变量值。
