nonlocal 与可变状态
修改外围作用域
nonlocal 与可变状态 是 CoddyKit 上的免费 Python Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。
读取和写入外层变量
inner 函数可以自由地读取外层变量,但如果它尝试赋值给该名称,Python 就会将其视为新的局部变量。这常常会让初学者感到意外。
def outer():
count = 0
def inner():
return count
return inner()
print(outer())赋值陷阱
如果不给出声明意图就对外层名称赋值,Python 会将它视为局部名称,因此外层变量不会被更新。这里的修改会丢失。
def outer():
count = 0
def bump():
count = count + 1
try:
bump()
except UnboundLocalError as e:
print('Error:', e)
outer()非局部关键字
nonlocal 声明某个名称指向最近的外层函数作用域中的变量,因此赋值会更新那个外层变量,而不是创建局部变量。
def outer():
count = 0
def bump():
nonlocal count
count += 1
bump()
bump()
return count
print(outer())计数器闭包
借助 nonlocal,您可以构建一个带状态的计数器。每次调用都会修改捕获的变量,并记住当前进度。
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
c = make_counter()
print(c())
print(c())
print(c())彼此独立的计数器
每个计数器都保存自己的状态,因为每个闭包捕获的是不同的变量。
def make_counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc
a = make_counter()
b = make_counter()
print(a(), a(), b())非局部变量与全局变量的区别
nonlocal 针对外层的函数作用域,而 global 针对模块级别。闭包应使用 nonlocal,模块范围的状态应使用 global。
total = 0
def add_global(n):
global total
total += n
add_global(5)
add_global(3)
print(total)不使用非局部声明的可变状态
如果外层的值是列表或字典这样的可变对象,您可以直接修改它,而不需要使用 nonlocal,因为您并没有重新绑定这个名称。
def make_logger():
history = []
def log(msg):
history.append(msg)
return history
return log
log = make_logger()
log('start')
print(log('stop'))累加器
将非局部变量与闭包结合起来,可以构建一个持续累加的累加器,将每个值加到记住的总数中。
def make_accumulator():
total = 0
def add(n):
nonlocal total
total += n
return total
return add
acc = make_accumulator()
print(acc(10))
print(acc(5))
print(acc(100))简单的切换器
非局部变量机制可以轻松地在多次调用之间切换布尔状态。
def make_toggle():
on = False
def toggle():
nonlocal on
on = not on
return on
return toggle
t = make_toggle()
print(t(), t(), t())重置状态
您可以公开多个共享同一外层状态的 inner 函数,例如一个用于 add,另一个用于 reset。
def make_bank():
balance = 0
def deposit(n):
nonlocal balance
balance += n
return balance
def reset():
nonlocal balance
balance = 0
return balance
return deposit, reset
deposit, reset = make_bank()
print(deposit(50))
print(reset())非局部变量需要已有变量
nonlocal 要求该名称已经存在于某个外层作用域中;否则 Python 会在定义时引发 SyntaxError。它无法创建新的外层变量。
def outer():
value = 'set'
def inner():
nonlocal value
value = 'changed'
inner()
return value
print(outer())快速检查
在嵌套函数中,如果您想重新给外层函数中的变量赋值,应使用哪个关键字?
回顾:非局部变量与可变状态
总结:
- 给外层名称赋值通常会创建新的局部变量;
nonlocal可以阻止这种情况。 nonlocal针对外层函数作用域;global针对模块。- 直接修改可变对象不需要任何声明。
- 结合
nonlocal的闭包可以构建能够记住状态的计数器、累加器和切换器。
def make_counter():
n = 0
def inc():
nonlocal n
n += 1
return n
return inc
c = make_counter()
print(c(), c())常见问题解答
「nonlocal 与可变状态」课时是免费的吗?
是的 — 「nonlocal 与可变状态」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。
「nonlocal 与可变状态」这节课中我会学到什么?
修改外围作用域 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Python Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「nonlocal 与可变状态」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Python Academy 课中编写并运行代码吗?
能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。