Python 中 global 和 nonlocal 的区别

FreeGuideOnline 最新 2026-07-07

作用域基础:先理解变量的地盘

在深入 globalnonlocal 之前,必须建立作用域的概念。Python 采用 LEGB 规则查找变量:

  • L (Local) – 当前函数内部
  • E (Enclosing) – 外层嵌套函数的局部作用域
  • G (Global) – 模块(文件)全局作用域
  • B (Built-in) – 内置作用域(如 printlen

当你在一个函数内给变量赋值时,Python 默认会创建一个新的局部变量,即使外层存在同名变量。globalnonlocal 就是用来自定义打破这个默认行为的声明。


global 关键字:直通全局作用域

作用

在函数或类内部声明一个变量为全局变量,后续对该变量的赋值与读取都直接操作全局作用域中的那个名字。

核心规则

  • 只有在需要修改全局变量的绑定(重新赋值)时才必须使用 global
  • 如果只是读取一个全局变量的值(如访问列表的元素、调用对象方法),无需 global
  • global 可以一次声明多个变量,用逗号隔开:global x, y, z

示例与解释

count = 0          # 全局变量

def increment():
    global count   # 声明下面使用的 count 是全局的那个
    count += 1     # 现在修改的是全局 count

def just_read():
    print(count)   # 只是读取,无需 global

increment()
print(count)       # 输出 1

如果去掉 global countcount += 1 会被视为局部变量未赋值就引用的错误(UnboundLocalError)。

典型使用场景

  • 函数需要维护跨多次调用的状态计数器。
  • 需要修改模块级别的配置标志(如 debug = True)。
  • 注意:滥用全局变量会让代码难以维护。优先考虑传参、返回值或类封装。

nonlocal 关键字:影响外层嵌套函数的变量

作用

嵌套函数内部,声明一个变量来自最近的、非全局的外层作用域(Enclosing 作用域)。它让内部函数可以重新绑定外层函数的局部变量。

核心规则

  • nonlocal 仅适用于嵌套函数结构(一个函数定义在另一个函数内部)。
  • 它不会去触碰全局作用域。如果外层没有该变量,则报错 SyntaxError
  • global 一样,只有需要重新赋值时才需要 nonlocal;仅读取外层变量时无需声明。

示例与解释

def outer():
    x = 10                     # outer 的局部变量(对于 inner 来说是 enclosing 变量)

    def inner():
        nonlocal x             # 声明 x 是外层函数的那个 x
        x += 5                 # 修改 outer 中的 x
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
# 输出:
# inner: 15
# outer: 15

去掉 nonlocal x 会触发 UnboundLocalError,因为 x += 5 需要先读取 x,此时 Python 会认为 x 是 inner 的局部变量,但尚未赋值。

如果需要修改的是可变对象内部的数据(如往列表里追加元素),则甚至不需要 nonlocal

def outer():
    data = []

    def inner():
        data.append(1)    # 修改的是列表内容,并非对 data 变量重新赋值,合法

    inner()
    print(data)           # 输出 [1]

多重嵌套时的行为

nonlocal 会逐层向外搜索,找到最近的那一层包含该变量的作用域:

def outer():
    x = "outer"
    def middle():
        x = "middle"
        def inner():
            nonlocal x        # 影响 middle 中的 x,而不是 outer 中的
            x = "modified"
        inner()
        print("middle:", x)
    middle()
    print("outer:", x)

outer()
# 输出:
# middle: modified
# outer: outer

典型使用场景

  • 闭包中修改外层自由变量(如计数器、缓存、状态机)。
  • 实现函数工厂或装饰器,需要在内部修改外部函数的配置。

global vs nonlocal 快查对照

关键词 作用范围 搜索顺序 典型场景
global 直接绑定到模块全局作用域 跳过所有局部作用域 修改模块级变量、标志位
nonlocal 绑定到最近的外层非全局作用域 从内向外找到第一个匹配 闭包内修改外层函数的局部变量

记忆口诀

  • global – 直达"地球全局"
  • nonlocal – 只影响"亲爹"(外层函数),绝不跨越到全局

常见陷阱与最佳实践

  1. 误以为只要看到外层变量就需要声明
    只有重新赋值=)才需要 global/nonlocal。对可变对象的内部修改不需声明。

  2. nonlocal 用在顶层函数
    nonlocal 必须在至少两层嵌套结构中使用,否则直接报 SyntaxError

  3. 同一函数内混合使用
    globalnonlocal 不能同时作用于同一个变量名,否则逻辑矛盾。

  4. 循环引入或过度使用
    大量依赖全局状态或闭包自由变量会降低代码可读性和可测试性。优先考虑:

    • 返回值传递状态
    • 将状态封装在类实例中
    • 对于闭包内的计数器,可使用 itertools.count 或可变容器
  5. 在模块顶层使用 nonlocal
    模块顶层没有外层非全局作用域,nonlocal 会立即失败。


一图总结:作用域修改选择流程

当你需要在函数内部修改一个外层变量的值时,按以下思路判断:

  1. 该变量在哪个作用域?
    • 全局作用域 → 使用 global
    • 外层函数作用域(非全局) → 使用 nonlocal
    • 当前函数作用域 → 正常赋值即可
  2. 如果仅仅是读取值或调用对象方法,则不需要任何关键字。
  3. 如果设计过于依赖外部变量,停下来想想能否用参数传递或类来重构。

掌握这两个关键字,你就能精确控制 Python 的作用域行为,写出更灵活、更正确的闭包与函数。从今往后,UnboundLocalError 不再是迷雾,而是可以轻松解决的明确信号。