在 Python 中,闭包(Closure)是一种特殊的函数现象,它允许嵌套函数访问外层函数中定义的变量,即使外层函数已经执行完毕。
一、闭包
闭包需要满足以下三个条件:
- 存在嵌套函数结构(函数内部定义另一个函数)
- 内层函数引用了外层函数的变量
- 外层函数返回了内层函数
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、注意事项
- 使用
nonlocal
关键字修改外层函数的变量 - 闭包会保留外层变量的引用,可能导致内存占用问题
- 每个闭包实例拥有独立的变量环境
闭包是 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
的使用规则
nonlocal
声明的变量必须在外层嵌套函数中已经定义,否则会报错nonlocal
不能用于声明全局变量nonlocal
声明的变量会向上查找最近的外层函数作用域,直到找到该变量- 在单一函数中不能同时用
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 的嵌套函数提供了更灵活的变量作用域控制,尤其在实现闭包和需要在嵌套结构中共享状态时非常有用。