跳转到内容

Python 闭包

来自代码酷

Python闭包[编辑 | 编辑源代码]

简介[编辑 | 编辑源代码]

闭包(Closure)是Python函数式编程中的一个重要概念,指的是一个函数能够记住并访问其词法作用域(lexical scope)中的变量,即使该函数在其词法作用域之外执行。闭包由两个主要部分组成:

  • 一个嵌套函数(内部函数)
  • 该嵌套函数引用的外部函数(封闭函数)的变量

闭包在Python中广泛应用于装饰器、回调函数和函数工厂等场景。

基本概念[编辑 | 编辑源代码]

闭包的核心在于词法作用域(lexical scoping),即函数在定义时(而非调用时)确定其作用域。当一个嵌套函数引用了其外部函数的变量时,Python会创建一个闭包来保存这些变量的状态。

闭包的形成条件[编辑 | 编辑源代码]

1. 必须有一个嵌套函数(内部函数定义在外部函数内) 2. 内部函数必须引用外部函数的变量 3. 外部函数必须返回内部函数

基础示例[编辑 | 编辑源代码]

以下是一个简单的闭包示例:

def outer_function(msg):
    # 外部函数的变量
    message = msg
    
    def inner_function():
        # 内部函数引用外部函数的变量
        print(message)
    
    # 返回内部函数(不执行)
    return inner_function

# 创建闭包
my_closure = outer_function("Hello, Closure!")
# 调用闭包
my_closure()  # 输出: Hello, Closure!

输出:

Hello, Closure!

在这个例子中: 1. `outer_function`是外部函数,它定义了变量`message`和内部函数`inner_function` 2. `inner_function`引用了外部函数的`message`变量 3. `outer_function`返回`inner_function`(注意没有括号,返回的是函数对象而非调用结果) 4. 当`my_closure()`被调用时,它仍然可以访问`message`变量,尽管`outer_function`已经执行完毕

闭包与变量作用域[编辑 | 编辑源代码]

闭包中的变量具有特殊的生命周期和作用域规则:

graph LR A[外部函数] --> B[定义变量x] A --> C[定义内部函数] C --> D[引用变量x] A --> E[返回内部函数] E --> F[闭包] F --> G[调用时仍可访问x]

闭包中的变量具有以下特点:

  • 闭包变量在外部函数执行完毕后仍然存在
  • 每次调用外部函数都会创建一个新的闭包作用域
  • 闭包变量可以被内部函数修改(在Python 3中使用`nonlocal`关键字)

修改闭包变量[编辑 | 编辑源代码]

在Python 3中,可以使用`nonlocal`关键字来修改闭包变量:

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

# 创建计数器闭包
my_counter = counter()
print(my_counter())  # 输出: 1
print(my_counter())  # 输出: 2
print(my_counter())  # 输出: 3

输出:

1
2
3

实际应用案例[编辑 | 编辑源代码]

案例1:函数工厂[编辑 | 编辑源代码]

闭包可以用于创建特定行为的函数:

def power_factory(exponent):
    def power(base):
        return base ** exponent
    return power

# 创建平方函数
square = power_factory(2)
# 创建立方函数
cube = power_factory(3)

print(square(4))  # 输出: 16
print(cube(4))    # 输出: 64

输出:

16
64

案例2:装饰器基础[编辑 | 编辑源代码]

Python装饰器本身就是闭包的应用:

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logger
def greet(name):
    print(f"Hello, {name}!")

greet("World")

输出:

调用函数: greet
Hello, World!

案例3:状态保持[编辑 | 编辑源代码]

闭包可以用于保持状态而无需使用类:

def make_accumulator():
    total = 0
    
    def accumulator(n):
        nonlocal total
        total += n
        return total
    
    return accumulator

acc = make_accumulator()
print(acc(10))  # 输出: 10
print(acc(20))  # 输出: 30
print(acc(30))  # 输出: 60

输出:

10
30
60

闭包的高级特性[编辑 | 编辑源代码]

查看闭包变量[编辑 | 编辑源代码]

可以使用`__closure__`属性查看闭包捕获的变量:

def outer(x):
    def inner():
        return x
    return inner

closure = outer(10)
print(closure.__closure__[0].cell_contents)  # 输出: 10

输出:

10

多个闭包变量[编辑 | 编辑源代码]

闭包可以捕获多个变量:

def multi_var_closure(a, b):
    def inner():
        return a + b
    return inner

closure = multi_var_closure(5, 7)
print(closure())  # 输出: 12

输出:

12

数学表达[编辑 | 编辑源代码]

从数学角度看,闭包可以表示为: 解析失败 (语法错误): {\displaystyle f: X \rightarrow (Y \rightarrow Z) \\ \text{其中闭包 } c = f(x) \text{ 满足 } c(y) = g(x, y) } 这里:

  • f是外部函数
  • c是返回的闭包
  • g是内部函数实现

常见问题[编辑 | 编辑源代码]

延迟绑定问题[编辑 | 编辑源代码]

在循环中创建闭包时可能出现意外行为:

def create_closures():
    closures = []
    for i in range(3):
        def closure():
            return i
        closures.append(closure)
    return closures

closures = create_closures()
print([c() for c in closures])  # 输出: [2, 2, 2] 而非预期的 [0, 1, 2]

解决方法:

def create_closures_fixed():
    closures = []
    for i in range(3):
        def closure(i=i):
            return i
        closures.append(closure)
    return closures

closures = create_closures_fixed()
print([c() for c in closures])  # 输出: [0, 1, 2]

性能考虑[编辑 | 编辑源代码]

  • 闭包比普通函数有轻微的性能开销
  • 闭包变量访问比局部变量慢,但比全局变量快
  • 在性能关键代码中应谨慎使用

总结[编辑 | 编辑源代码]

Python闭包是函数式编程中的强大工具,它允许函数:

  • 记住并访问其词法作用域中的变量
  • 保持状态而不使用全局变量
  • 创建灵活的函数工厂和装饰器

理解闭包对于掌握Python高级特性如装饰器和函数式编程模式至关重要。通过合理使用闭包,可以编写出更加模块化和可维护的代码。