python 闭包

2025 年 04 月 21 日
6 次浏览
3638 字数

闭包(Closure)是编程中一个重要的概念,尤其在函数式编程中广泛应用。它的核心是 函数与其引用环境的结合,使得函数可以“记住”并访问其定义时的上下文环境,即使这个环境在函数被调用时已经不存在了。


什么是闭包?

闭包是一个 函数对象,它保留了对其定义时的 词法作用域(lexical scope) 中变量的引用。简单来说:

  1. 内部函数:闭包通常是嵌套在另一个函数内部的函数。
  2. 引用外部变量:内部函数引用了外部函数(或父函数)的变量。
  3. 保持变量存活:即使外部函数执行完毕,其变量依然被内部函数保留。

为什么叫“闭包”?

  • “闭”:指函数“封闭”了其定义时的环境(变量),将这些变量“包裹”在自身的作用域内。
  • “包”:指函数和其引用的环境(变量)共同组成了一个“包”。
  • 英文原意:术语来自数学中的“闭包”(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
    

闭包的用途

  1. 封装私有数据:避免使用全局变量,隐藏实现细节。
    def counter():
        count = 0
        def increment():
            nonlocal count
            count += 1
            return count
        return increment
    
    c = counter()
    print(c())  # 1
    print(c())  # 2
    
  2. 延迟计算:在需要时再执行某些逻辑。
  3. 装饰器(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
    

注意事项

  1. 变量绑定时机:闭包中引用的外部变量是 延迟绑定 的,可能产生意外结果(尤其在循环中)。

    # 错误示例:循环中创建闭包
    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)
    
  2. 内存泄漏:闭包可能导致外部变量无法被回收,需谨慎使用。


总结

  • 闭包是函数和其引用环境的结合体,用于保留上下文变量。
  • 名称源于其“封闭并包裹”外部环境变量的特性。
  • 广泛应用于状态保存、装饰器、回调函数等场景。