Python中的闭包(Closure)
字数 1030 2025-11-03 20:46:32

Python中的闭包(Closure)

闭包是指在一个函数内部定义了另一个函数,并且这个内部函数引用了外部函数的变量(非全局变量),同时外部函数的返回值为内部函数。这样,内部函数以及它所引用的外部变量一起构成了一个闭包。

1. 闭包的基本概念

  • 闭包的形成需要满足三个条件:
    • 必须有一个内嵌函数(函数内部定义的函数)。
    • 内嵌函数必须引用外部函数中定义的变量(即自由变量)。
    • 外部函数必须返回内嵌函数(而不是调用它)。

2. 闭包的简单示例
假设我们需要一个函数,每次调用时能记录并返回被调用的次数:

def counter():
    count = 0  # 外部函数的变量
    def inner():
        nonlocal count  # 声明count为非局部变量,以便修改它
        count += 1
        return count
    return inner  # 返回内部函数,而不是调用结果

# 创建闭包实例
c = counter()
print(c())  # 输出: 1
print(c())  # 输出: 2
  • 这里,counter是外部函数,inner是内嵌函数。
  • inner引用了外部变量countcounter返回inner函数。
  • 当调用c = counter()时,c实际上指向inner函数,但保留了count变量的状态,即使counter函数已执行完毕。

3. 闭包的工作原理

  • 在Python中,函数是一等对象,可以像变量一样传递和返回。
  • 当外部函数执行时,会创建一个局部作用域,包含其局部变量(如count)。
  • 内部函数会记住其定义时的作用域(即外部函数的局部作用域),即使外部函数已结束,内部函数仍能访问这些变量。
  • 这种“记忆”是通过函数的__closure__属性实现的,它保存了所有引用的外部变量。

4. 查看闭包的细节
使用__closure__属性可以观察闭包引用的变量:

def example(x):
    def inner(y):
        return x + y
    return inner

f = example(10)
print(f.__closure__)        # 输出: (<cell at 0x...: int object at 0x...>,)
print(f.__closure__[0].cell_contents)  # 输出: 10(即外部变量x的值)
  • __closure__是一个元组,包含所有被引用的外部变量的“单元格”(cell)。
  • 每个单元格的cell_contents属性存储了变量的实际值。

5. 闭包与可变状态

  • 如果闭包需要修改外部变量,必须使用nonlocal声明(Python 3以上):
def accumulator(start=0):
    total = start
    def add(n):
        nonlocal total  # 声明total为非局部变量
        total += n
        return total
    return add

acc = accumulator(10)
print(acc(5))  # 输出: 15
print(acc(3))  # 输出: 18
  • 如果没有nonlocaltotal += n会被视为在add内部创建新的局部变量,导致错误。

6. 闭包的常见应用场景

  • 状态保持:如计数器、累加器,避免使用全局变量。
  • 延迟计算:闭包可以先捕获一些参数,在后续调用时再执行计算。
  • 装饰器:装饰器本身就是闭包的一种应用,它接收函数作为参数,并返回一个新函数。

7. 注意事项

  • 闭包可能导致内存泄漏,因为它会长期持有外部变量的引用。
  • 在循环中创建闭包时,要小心变量绑定问题:
# 错误示例:所有闭包共享同一个i
functions = []
for i in range(3):
    def inner():
        return i
    functions.append(inner)
print([f() for f in functions])  # 输出: [2, 2, 2](不是预期的[0,1,2])

# 正确做法:使用默认参数捕获当前值
functions = []
for i in range(3):
    def inner(x=i):  # 用默认参数绑定当前i的值
        return x
    functions.append(inner)
print([f() for f in functions])  # 输出: [0, 1, 2]

通过以上步骤,闭包的核心机制和应用场景已清晰呈现。它通过函数嵌套和变量引用,实现了状态的封装和持久化。

Python中的闭包(Closure) 闭包是指在一个函数内部定义了另一个函数,并且这个内部函数引用了外部函数的变量(非全局变量),同时外部函数的返回值为内部函数。这样,内部函数以及它所引用的外部变量一起构成了一个闭包。 1. 闭包的基本概念 闭包的形成需要满足三个条件: 必须有一个内嵌函数(函数内部定义的函数)。 内嵌函数必须引用外部函数中定义的变量(即自由变量)。 外部函数必须返回内嵌函数(而不是调用它)。 2. 闭包的简单示例 假设我们需要一个函数,每次调用时能记录并返回被调用的次数: 这里, counter 是外部函数, inner 是内嵌函数。 inner 引用了外部变量 count , counter 返回 inner 函数。 当调用 c = counter() 时, c 实际上指向 inner 函数,但 保留了 count 变量的状态 ,即使 counter 函数已执行完毕。 3. 闭包的工作原理 在Python中,函数是一等对象,可以像变量一样传递和返回。 当外部函数执行时,会创建一个局部作用域,包含其局部变量(如 count )。 内部函数会 记住 其定义时的作用域(即外部函数的局部作用域),即使外部函数已结束,内部函数仍能访问这些变量。 这种“记忆”是通过函数的 __closure__ 属性实现的,它保存了所有引用的外部变量。 4. 查看闭包的细节 使用 __closure__ 属性可以观察闭包引用的变量: __closure__ 是一个元组,包含所有被引用的外部变量的“单元格”(cell)。 每个单元格的 cell_contents 属性存储了变量的实际值。 5. 闭包与可变状态 如果闭包需要修改外部变量,必须使用 nonlocal 声明(Python 3以上): 如果没有 nonlocal , total += n 会被视为在 add 内部创建新的局部变量,导致错误。 6. 闭包的常见应用场景 状态保持 :如计数器、累加器,避免使用全局变量。 延迟计算 :闭包可以先捕获一些参数,在后续调用时再执行计算。 装饰器 :装饰器本身就是闭包的一种应用,它接收函数作为参数,并返回一个新函数。 7. 注意事项 闭包可能导致内存泄漏,因为它会长期持有外部变量的引用。 在循环中创建闭包时,要小心变量绑定问题: 通过以上步骤,闭包的核心机制和应用场景已清晰呈现。它通过函数嵌套和变量引用,实现了状态的封装和持久化。