闭包(包含nonlocal、global关键字介绍)(Python高阶)

在 Python 中,闭包(Closure)是一种特殊的函数现象,它允许嵌套函数访问外层函数中定义的变量,即使外层函数已经执行完毕。


一、闭包

闭包需要满足以下三个条件:

  1. 存在嵌套函数结构(函数内部定义另一个函数)
  2. 内层函数引用了外层函数的变量
  3. 外层函数返回了内层函数

1、闭包的工作原理

当外层函数执行结束后,其作用域本应被销毁,但由于内层函数仍然引用着外层函数的变量,这些变量会被保留下来,形成一个 "闭包环境"。

2、简单示例

def outer_function(x):
    # 外层函数定义的变量
    a = x
    
    def inner_function(y):
        # 内层函数引用了外层函数的变量a
        return a + y
    
    # 外层函数返回内层函数
    return inner_function

# 创建闭包实例
closure = outer_function(10)

# 调用闭包,可以看到它仍然保留着外层函数的变量a=10
print(closure(5))  # 输出 15
print(closure(7))  # 输出 17

3、闭包的用途

1.数据封装与隐藏:可以创建私有变量,实现类似面向对象的封装特性

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

# 创建计数器
c1 = counter()
print(c1())  # 1
print(c1())  # 2

# 每个闭包实例拥有独立的环境
c2 = counter()
print(c2())  # 1

2.延迟计算:可以保存计算所需的参数,在需要时再执行

def make_operation(op):
    def calculate(a, b):
        if op == 'add':
            return a + b
        elif op == 'mul':
            return a * b
    return calculate

adder = make_operation('add')
multiplier = make_operation('mul')

print(adder(2, 3))      # 5
print(multiplier(2, 3)) # 6

3.装饰器基础:Python 装饰器的实现依赖于闭包特性

(详情见下一篇文章装饰器(python高阶)-CSDN博客

4、注意事项

  1. 使用 nonlocal 关键字修改外层函数的变量
  2. 闭包会保留外层变量的引用,可能导致内存占用问题
  3. 每个闭包实例拥有独立的变量环境

闭包是 Python 函数式编程的重要特性,合理使用可以使代码更加简洁、灵活,尤其在需要创建多个相似功能但状态不同的函数时非常有用。


二、nonlocal

在 Python 中,nonlocal 是一个用于声明变量作用域的关键字,主要用于嵌套函数中,帮助内层函数修改外层函数中定义的变量。它解决了在嵌套结构中变量作用域混淆的问题。

1、nonlocal 的基本作用

当内层函数需要修改外层函数中定义的变量时,单纯的引用会被视为创建新的局部变量,而 nonlocal 关键字可以明确指定:"这个变量不是当前函数的局部变量,而是来自外层嵌套函数的作用域"

2、没有 nonlocal 的问题

看下面的例子,尝试在内部函数修改外部函数的变量:

def outer():
    x = 10
    
    def inner():
        x = 20  # 这里实际上创建了一个新的局部变量x,而不是修改outer中的x
        print("inner x:", x)
    
    inner()
    print("outer x:", x)

outer()
# 输出:
# inner x: 20
# outer x: 10  # outer中的x并未被修改

可以看到,inner() 中的 x = 20 并没有修改 outer() 中的 x,而是创建了一个新的局部变量。

3、使用 nonlocal 解决问题

加上 nonlocal 关键字后:

def outer():
    x = 10
    
    def inner():
        nonlocal x  # 声明x来自外层函数
        x = 20      # 现在修改的是outer中的x
        print("inner x:", x)
    
    inner()
    print("outer x:", x)

outer()
# 输出:
# inner x: 20
# outer x: 20  # outer中的x被成功修改

现在 inner() 函数可以正确修改 outer() 中定义的 x 变量了。

4、nonlocal 与 global 的区别

  • nonlocal 用于修改外层嵌套函数中的变量(不是全局变量)
  • global 用于修改全局作用域中的变量

对比示例:

# 全局变量
g = 100

def outer():
    # 外层函数变量
    o = 10
    
    def inner():
        global g    # 声明使用全局变量g
        nonlocal o  # 声明使用外层函数变量o
        
        g = 200
        o = 20
        
        print("inner g:", g)
        print("inner o:", o)
    
    inner()
    print("outer o:", o)

outer()
print("global g:", g)

# 输出:
# inner g: 200
# inner o: 20
# outer o: 20
# global g: 200

5、nonlocal 的使用规则

  1. nonlocal 声明的变量必须在外层嵌套函数中已经定义,否则会报错
  2. nonlocal 不能用于声明全局变量
  3. nonlocal 声明的变量会向上查找最近的外层函数作用域,直到找到该变量
  4. 在单一函数中不能同时用 nonlocal 和普通赋值创建同名变量

6、典型应用场景:闭包中的状态保持

nonlocal 最常见的用途是在闭包中维护和修改状态:

def make_counter():
    count = 0  # 外层函数变量
    
    def counter():
        nonlocal count  # 声明使用外层的count变量
        count += 1
        return count
    
    return counter

# 创建计数器实例
c1 = make_counter()
print(c1())  # 1
print(c1())  # 2
print(c1())  # 3

# 每个实例拥有独立的状态
c2 = make_counter()
print(c2())  # 1

这个例子中,nonlocal 允许 counter() 函数每次调用时都能修改 make_counter() 中定义的 count 变量,从而实现了状态的持续跟踪。


总结来说,nonlocal 关键字为 Python 的嵌套函数提供了更灵活的变量作用域控制,尤其在实现闭包和需要在嵌套结构中共享状态时非常有用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值