0Pricing
Python Academy · 课时

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 一等函数
  2. 闭包与自由变量
  3. nonlocal 与可变状态
  4. 偏函数
← 返回 Python Academy