python 闭包
闭包(Closure)是编程中一个重要的概念,尤其在函数式编程中广泛应用。它的核心是 函数与其引用环境的结合,使得函数可以“记住”并访问其定义时的上下文环境,即使这个环境在函数被调用时已经不存在了。
什么是闭包?
闭包是一个 函数对象,它保留了对其定义时的 词法作用域(lexical scope) 中变量的引用。简单来说:
- 内部函数:闭包通常是嵌套在另一个函数内部的函数。
- 引用外部变量:内部函数引用了外部函数(或父函数)的变量。
- 保持变量存活:即使外部函数执行完毕,其变量依然被内部函数保留。
为什么叫“闭包”?
- “闭”:指函数“封闭”了其定义时的环境(变量),将这些变量“包裹”在自身的作用域内。
- “包”:指函数和其引用的环境(变量)共同组成了一个“包”。
- 英文原意:术语来自数学中的“闭包”(closure),表示一个集合对某种操作的封闭性。在编程中,闭包确保函数能够访问其定义时的环境变量,即使环境已销毁。
闭包的典型示例
def outer():
x = 10 # 外部函数的局部变量
def inner():
print(x) # 内部函数引用了外部变量 x
return inner # 返回内部函数(闭包)
# 调用 outer(),返回 inner 函数
closure_func = outer()
closure_func() # 输出 10,即使 outer() 已经执行完毕,x 仍然被保留
关键点:
inner
是闭包,因为它“记住”了outer
的局部变量x
。- 即使
outer()
执行结束,x
的生命周期被延长,直到closure_func
不再被使用。
闭包的本质
闭包的实现依赖于 词法作用域(Lexical Scoping):
- Python 解释器会检查内部函数是否引用了外部变量。
- 如果引用了,会将这些变量存储在
__closure__
属性中(一个包含变量的元组)。 - 例如,查看闭包的变量:
print(closure_func.__closure__) # 输出类似 <cell at 0x...: int object at 0x...> print(closure_func.__closure__[0].cell_contents) # 输出 10
闭包的用途
- 封装私有数据:避免使用全局变量,隐藏实现细节。
def counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment c = counter() print(c()) # 1 print(c()) # 2
- 延迟计算:在需要时再执行某些逻辑。
- 装饰器(Decorators):Python 装饰器的核心机制就是闭包。
def logger(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__}") return func(*args, **kwargs) return wrapper
闭包 vs 类
闭包可以替代简单的类,用于保存状态:
- 类的实现:
class Counter: def __init__(self): self.count = 0 def increment(self): self.count += 1 return self.count
- 闭包的实现(更简洁):
def counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment
注意事项
-
变量绑定时机:闭包中引用的外部变量是 延迟绑定 的,可能产生意外结果(尤其在循环中)。
# 错误示例:循环中创建闭包 funcs = [] for i in range(3): def func(): print(i) funcs.append(func) # 所有函数都会输出 2,因为 i 最终是 2
修复方法:立即绑定变量。
for i in range(3): def func(j=i): # 使用默认参数立即绑定当前值 print(j) funcs.append(func)
-
内存泄漏:闭包可能导致外部变量无法被回收,需谨慎使用。
总结
- 闭包是函数和其引用环境的结合体,用于保留上下文变量。
- 名称源于其“封闭并包裹”外部环境变量的特性。
- 广泛应用于状态保存、装饰器、回调函数等场景。