Python闭包
闭包概念
闭包:由外部函数和内部函数嵌套构成,内部函数引用外部函数的变量,且外部函数返回内部函数
def outer(x): # 外部函数
def inner(y): # 内部函数
return x + y # 引用外部变量x
return inner # 返回内部函数
closure = outer(10) # 闭包生成(x=10被捕获)
print(closure(5)) # 输出15(10+5)
闭包的工作机制
变量捕获与生命周期
- 外部函数的变量被保存在内部函数的closure属性中(类型为cell对象)。
- 即使外部函数执行完毕,其变量仍被闭包持有,不会被销毁
修改变量的限制
需用nonlocal声明才能修改外部变量,否则视为创建局部变量
def counter():
count = 0
def increment():
nonlocal count # 声明修改外部变量
count += 1
return count
return increment
常见问题
延迟绑定
闭包捕获的是变量的引用而非当前值,导致调用时访问的是变量最终的状态
def create_funcs():
funcs = []
for i in range(3):
def f():
return i # 捕获变量i的引用
funcs.append(f)
return funcs
f1, f2, f3 = create_funcs()
print(f1(), f2(), f3()) # 输出:2 2 2(非预期的0,1,2)
所有闭包共享变量i,调用时i已变为2
解决方案
默认参数绑定:默认参数在函数定义时求值,立即绑定当前值。
def create_funcs_fixed():
funcs = []
for i in range(3):
def f(x=i): # 默认参数x绑定当前i的值
return x
funcs.append(f)
return funcs
f1, f2, f3 = create_funcs_fixed()
print(f1(), f2(), f3()) # 输出:0 1 2
闭包捕获可变对象
闭包捕获的是对象的引用(内存地址),而非对象的副本,多个闭包访问的是同一内存地址的数据
def create_closures():
data = [1, 2, 3] # 可变对象
closure1 = lambda: data.append(4)
closure2 = lambda: print(data)
return closure1, closure2
c1, c2 = create_closures()
c1() # 修改列表
c2() # 输出 [1, 2, 3, 4] (共享同一列表)
解决方案
若捕获不可变对象(如整数、字符串),闭包会保存其值而非引用,修改时会创建新对象
def create_closure():
x = 10 # 不可变对象
closure = lambda: print(x)
x = 20 # 创建新对象,不影响闭包
return closure
c = create_closure()
c() # 输出 10(闭包捕获的是初始值)
可变对象作为函数默认参数
默认参数在函数定义时仅计算一次,立即绑定当前值,当默认值为可变对象时,所有调用共享同一对象引用
def append_to(element, arr=[]): # arr在定义时创建,后续调用共享同一列表
arr.append(element)
return arr
print(append_to(1)) # 输出 [1]
print(append_to(2)) # 输出 [1, 2](而非预期的[2])
解决方案
使用None代替可变默认值
def append_to(element, arr=None):
if arr is None: # 每次调用创建新列表
arr = []
arr.append(element)
return arr
浙公网安备 33010602011771号