Python 闭包(Closure)详解

6 阅读3分钟

一、认识闭包

1. 问题背景

在 Python 中,函数外部无法直接访问函数内部的局部变量,这是由 Python 的链式作用域决定的。

python

def f1():
    n = 999

print(n)  # 报错:name 'n' is not defined

2. 解决方案:函数嵌套

在函数内部定义嵌套函数,利用作用域特性实现外部访问。

python

def f1():
    n = 999
    def f2():
        print(n)
    return f2

result = f1()
result()  # 输出:999

二、闭包的概念

1. 通俗定义

闭包 = 内层嵌套函数 + 引用外层函数变量 + 作为返回值

2. 严谨定义(维基百科)

在支持函数嵌套的语言中,如果内部函数引用了外部函数的变量,就可能产生闭包。闭包可以在函数与一组 “私有” 变量之间建立关联,让这些变量在多次调用中保持持久。

3. 闭包两大核心作用

  1. 作为桥梁,读取函数内部的变量
  2. 将外层函数变量持久保存在内存中

三、闭包的用途

(一) 需要“记住”状态,但不想用全局变量或类

隐藏变量、保持命名空间干净,同时对外提供访问入口。

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

c1 = make_counter()
print(c1())  # 1
print(c1())  # 2

(二)数据隐藏 / 封装私有变量

def create_account(initial_balance):
    balance = initial_balance
    def withdraw(amount):
        nonlocal balance
        if amount <= balance:
            balance -= amount
            return balance
        return "Insufficient funds"
    def deposit(amount):
        nonlocal balance
        balance += amount
        return balance
    return withdraw, deposit  # 只能通过返回的函数操作 balance

(三)延迟计算 / 惰性求值

def lazy_sum(*args):
    def calc():
        return sum(args)
    return calc  # 不立即计算,调用时才计算

f = lazy_sum(1,2,3,4)
print(f())  # 真正计算时调用

(四)函数工厂(动态生成不同行为的函数)

def make_power(n):
    def power(x):
        return x ** n
    return power

square = make_power(2)
cube = make_power(3)
print(square(5))  # 25
print(cube(5))    # 125

四、使用闭包的注意事项

1. 内存消耗问题

闭包会将变量常驻内存,滥用会导致内存泄漏、性能下降。解决办法:不使用时及时清理无用变量。

2. 适用场景

  • 功能简单、只有一个方法:优先用闭包,代码更简洁
  • 功能复杂、多方法:优先用面向对象(类)
  • 学习闭包核心价值:彻底理解装饰器

3. 不可变类型变量修改问题

内层函数默认不能直接修改外层 int/str/tuple 等不可变变量,会创建新局部变量。必须使用 nonlocal 声明。

python

def outer_fun():
    x = 0
    def inner_fun():
        nonlocal x
        x = 1
        print('inner x:', x)
    inner_fun()
    print('outer x:', x)

outer_fun()  # 输出 1 和 1

4. 禁止引用循环变量 / 后续变化变量

返回闭包时不要引用循环变量,否则所有函数都会取最终值

python

# 错误示例
def count():
    fs = []
    for i in range(1,4):
        def f():
            return i*i
        fs.append(f)
    return fs

f1,f2,f3 = count()
print(f1(),f2(),f3())  # 9 9 9

# 正确示例:用参数绑定当前值
def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1,4):
        fs.append(f(i))
    return fs

f1,f2,f3 = count()
print(f1(),f2(),f3())  # 1 4 9

五、如何判断一个函数是闭包

查看函数的 closure 属性:

  • 闭包会返回一个由 cell 对象组成的元组
  • 通过 cell_contents 可查看捕获的变量

python

def add(x,y):
    def f(z):
        return x+y+z
    return f

d = add(5,6)
print(d.__closure__)
for cell in d.__closure__:
    print(cell.cell_contents)  # 输出 5、6

六、闭包核心总结

  1. 闭包 = 内层函数 + 引用外层变量 + 返回内层函数
  2. 作用:读内部变量、持久化保存变量
  3. 注意:内存消耗、nonlocal、避免循环变量
  4. 地位:Python 装饰器的基础,进阶必备