Python 为什么闭包中修改外部变量要用 nonlocal?

1次阅读

Python 闭包中修改外层变量需用 nonlocal 声明,否则赋值会触发 UnboundLocalError;因赋值使变量默认为局部变量,而读取时按 LEGB 规则查找,nonlocal 显式声明可变闭包以保障代码明确性。

Python 为什么闭包中修改外部变量要用 nonlocal?

因为 Python 默认把函数内赋值的变量当作局部变量,即使同名变量在外部 作用域 已存在,不加 nonlocal 就无法在闭包中修改外层函数的变量

作用域规则决定了赋值行为

Python 遵循 LEGB 规则(Local → Enclosing → Global → Built-in),但 只读访问和写入访问的处理方式不同

  • 读取变量时,会按 LEGB 顺序向上查找
  • 一旦在函数体内对某个变量执行了赋值(如 x = x + 1),Python 就默认这个变量是当前函数的局部变量
  • 此时若该变量在外部作用域存在,但未声明为 nonlocalglobal,就会报 UnboundLocalError

闭包中的典型错误示例

比如想实现一个计数器:

def make_counter():     count = 0     def counter():         count += 1  # ❌ 报错:UnboundLocalError         return count     return counter 

c = make_counter() c() # UnboundLocalError: local variable 'count' referenced before assignment

这里 count += 1 等价于 count = count + 1,触发了“赋值即局部变量”的规则。虽然读取 count 时能找到外层的 count,但一赋值就要求它必须是局部变量——而它又没被初始化,所以出错。

立即学习Python 免费学习笔记(深入)”;

nonlocal 的作用就是显式声明“我要修改的是外层非全局变量”

修正方式很简单:

def make_counter():     count = 0     def counter():         nonlocal count  # ✅ 告诉 Python:这个 count 是外层函数的变量         count += 1         return count     return counter

  • nonlocal 只能用于嵌套函数中,且目标变量必须存在于外层(enclosing)作用域,不能是全局变量
  • 如果是想改模块级变量,要用 global,而不是 nonlocal
  • nonlocal 声明后,对该变量的读、写、del 都操作的是外层变量,真正构成可变闭包

为什么 设计成这样?

这是 Python 明确性原则的体现:

  • 避免无意中修改外层状态,提升代码可预测性
  • 强制开发者意识到“我在跨作用域修改”,而不是隐式地影响上层逻辑
  • 区分只读闭包(无需声明)和可变闭包(需显式声明),语义更清晰

text=ZqhQzanResources