> 技术文档 > Python:闭包的概念与应用

Python:闭包的概念与应用

在 Python 函数式编程中,闭包是一种核心机制,它能让函数“记住”外部函数的变量,即使外部函数执行结束,这些变量依然可用。

理解闭包对于掌握装饰器、高阶函数、延迟计算等高级技巧非常重要。

一、什么是闭包?

闭包(Closure)是由函数及其相关的引用环境组成的对象

简而言之,闭包是“定义在另一个函数内部的函数”,并且该内部函数引用了外部函数的局部变量。即使外部函数返回后,这些变量也不会被销毁。

闭包成立的三个条件:

1、嵌套函数:在一个函数中定义另一个函数。

2、变量引用:内部函数引用外部函数的局部变量(也称“自由变量”)。

3、返回内部函数:外部函数将内部函数返回给外部。

示例:

def outer(x):              # 外部函数,接收一个参数 x    def inner(y):          # 内部函数,接收一个参数 y        return x + y       # 引用了外部函数的变量 x    return inner           # 返回内部函数对象f = outer(10)              # outer(10) 返回 inner,并且记住 x=10print(f(5))   # 15         # 等价于 inner(5),即 10 + 5print(f(20))  # 30         # 再次调用,仍然使用 x=10

二、闭包的工作机制

闭包对象本质上是:函数对象 + 自由变量引用环境。

验证:

def outer(x):    def inner(y):        return x + y    return innerf = outer(10)print(type(f))         # print(f.__closure__)                  # 查看闭包单元(cell 对象)元组print(f.__closure__[0].cell_contents) # 10,闭包保存的自由变量值

虽然闭包依然是 function 类型,但其 __closure__ 属性中保存了自由变量的引用。

1、变量生命周期

当外层函数执行结束后,被内部函数引用的自由变量会以 cell 对象的形式保存在函数对象的 __closure__ 属性中,后续调用闭包时依然可用。

2、变量解析顺序

闭包访问变量时遵循 LEGB 顺序,其中 E(Enclosing)就是自由变量所在的“嵌套作用域”。

3、变量修改限制

嵌套作用域中的自由变量默认不能直接赋值,否则 Python 会认为你是在内部函数中新建同名的局部变量。

如果确实要修改,可以使用 nonlocal 显式声明:

def outer(x):    def inner(y):        nonlocal x    # 声明 x 不是本函数的局部变量        x += 1          return x + y    return innerf = outer(10)print(f(10))  # 21print(f.__closure__[0].cell_contents) # 11,闭包保存的自由变量值

4、延迟绑定问题(Late Binding)

闭包保存的是变量的绑定,在调用时才解析变量的值。

循环中创建闭包时,所有闭包共享同一个变量绑定,导致取到相同的值。

funcs = []                              # 用于存放函数对象的列表for i inrange(3):# lambda 捕获的是变量 i 的引用,而不是当下的值    funcs.append(lambda: i)# 循环结束时 i=2,三个闭包共享同一个 iprint([f() for f in funcs])  # [2, 2, 2] ❌

解决方法:在定义时通过默认参数绑定当前值

funcs = []for i in range(3):    funcs.append(lambda i=i: i)        # i=i 将当前值绑定到默认参数print([f() for f in funcs])  # [0, 1, 2] ✅

三、闭包的常见应用场景

1、延迟计算(Lazy Evaluation)

延迟计算的核心思想是:先保存计算逻辑,等需要结果时再执行。

闭包可以把数据和计算过程绑定在一起,实现按需计算。

def lazy_sum(*args):    def compute():        print(\"正在执行计算...\")        return sum(args)    return compute  # 返回计算函数,而不是结果f = lazy_sum(1, 2, 3, 4, 5)print(\"计算未执行\")     # 此时还未计算print(f())             # 调用时才计算 -> 输出 15

2、参数缓存(Memoization)

参数缓存的目的是:保存已经计算过的结果,避免重复计算,提高性能。

闭包可作为轻量级的缓存系统。

def memoized_square():    cache = {}    def square(n):        if n not in cache:            print(f\"计算 {n} 的平方...\")            cache[n] = n * n        return cache[n]    return squaresquare = memoized_square()print(square(4))  # 第一次计算 -> 输出 16print(square(4))  # 直接取缓存 -> 输出 16(无计算过程)print(square(5))  # 新计算 -> 输出 25

3、函数工厂(Function Factory)

闭包可以用来动态创建功能类似但参数不同的函数。

def make_power(exp):                  # 外部函数接收指数 exp    def power(base):                   # 内部函数接收底数 base        return base ** exp             # 计算 base 的 exp 次方    return power                       # 返回内部函数square = make_power(2)                 # 创建平方函数cube = make_power(3)                   # 创建立方函数print(square(4))  # 16  -> 4 ** 2print(cube(2))    # 8   -> 2 ** 3

四、补充说明

1、Python 中的装饰器(decorator)的本质就是闭包。装饰器语法只是对闭包的简化写法。

2、闭包会延长自由变量的生命周期,如果闭包对象长期存在,这些变量也会常驻内存。

建议:避免在闭包中保存过大的数据结构或引用不必要的对象,以防止内存占用过高。

3、闭包过多会让代码逻辑难以跟踪,尤其是在多层嵌套的情况下。对于复杂逻辑,考虑改为类(使用 __call__ 方法)或普通函数。

📘 小结

闭包让函数拥有“记忆”,即便外部作用域结束,变量依然存在。

闭包是装饰器、高阶函数等高级特性的基础。

使用闭包时要注意延迟绑定问题和内存占用。

图片

“点赞有美意,赞赏是鼓励”