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
posted @ 2025-08-28 14:56  xclic  阅读(38)  评论(0)    收藏  举报