2025-10-07 Python不基础 20——全局变量与自由变量

文章目录

  • [1. Python 变量作用域](#1. Python 变量作用域)
  • [2. `global` 关键字](#2. global 关键字)
  • [3. `nonlocal` 关键字](#3. nonlocal 关键字)
  • [4. 修改 vs 赋值](#4. 修改 vs 赋值)
  • [5. 总结](#5. 总结)

本文参考视频链接:

在 Python 中,函数对全局变量的操作存在一个反直觉的特性:仅读取全局变量时正常,若尝试赋值则会报错。我们通过具体案例拆解这一现象。

定义全局变量 count,函数内仅打印(读取)该变量,程序正常执行:

python 复制代码
# 定义全局变量
count = 0

def f():
    # 读取全局变量 count
    print(count)

f()  # 输出:0(无报错,正常读取)

若在函数内添加对 count 的赋值操作,即使先读取后赋值,程序也会报错:

python 复制代码
count = 0

def f():
    print(count)  # 看似读取全局变量
    count = count + 1  # 对 count 赋值

f()  # 报错:UnboundLocalError: local variable 'count' referenced before assignment

报错原因:Python 认为函数内的 count局部变量 ,但 print(count) 执行时,局部变量 count 尚未赋值,因此抛出"引用未赋值变量"的错误。

1. Python 变量作用域

上述现象的核心是 Python 编译期的变量作用域判断逻辑,而非运行时动态判断。

Python 在编译函数代码时,会扫描函数内部是否存在对某个变量的赋值操作 (变量出现在 = 左侧):

  • 存在赋值操作 :Python 默认该变量是局部变量,函数内所有对该变量的操作都指向局部变量;
  • 不存在赋值操作:Python 会向上查找变量(先局部→再嵌套作用域→再全局→最后内置),此时读取的是全局变量。

关键:判断依据是"是否有赋值操作",与赋值语句的位置顺序无关(即使赋值在读取之后,仍默认变量为局部)。

Python 编译后的字节码能直观体现这一规则:

  • 仅读取全局变量 (案例1):函数内使用 load_global 指令,从全局作用域加载变量;

  • 函数内赋值变量 (案例2):函数内使用 load_fast 指令,从局部作用域加载变量(但局部变量未赋值,故报错)。

简言之:赋值操作让 Python 把变量"标记"为局部,读取时优先找局部作用域,找不到则报错。

2. global 关键字

若需在函数内修改全局变量 ,必须通过 global 关键字显式声明:"该变量是全局变量,而非局部变量"。

在函数开头添加 global count,明确变量归属,程序可正常修改全局变量:

python 复制代码
count = 0

def f():
    # 显式声明:count 是全局变量
    global count
    print(count)  # 读取全局变量(此时指向全局)
    count = count + 1  # 修改全局变量

f()  # 输出:0
print(count)  # 输出:1(全局变量已被修改)

global 的作用:

  • 告诉 Python 编译器:函数内的 count 不是局部变量,而是全局作用域中定义的变量;
  • 函数内对 count 的赋值操作,会直接修改全局变量,而非创建局部变量。

3. nonlocal 关键字

函数嵌套(闭包)场景中,内层函数对"外层函数的局部变量"的操作,与全局变量问题类似:仅读取正常,赋值则默认内层局部变量

外层函数 g 定义变量 count,内层函数 f 尝试赋值该变量,结果要么报错,要么修改无效:

python 复制代码
def g():
    # 外层函数的局部变量(非全局)
    count = 0

    def f():
        # 尝试修改外层函数的 count
        count = 1  # 赋值操作→默认是内层函数的局部变量

    f()
    print(count)  # 输出:0(外层 count 未被修改,内层修改的是局部变量)

若先读取后赋值,同样会报错(与全局变量案例2逻辑一致):

python 复制代码
def g():
    count = 0

    def f():
        print(count)  # 内层局部变量 count 未赋值
        count = 1

    f()  # 报错:UnboundLocalError: local variable 'count' referenced before assignment

global 用于声明"全局变量",而 nonlocal 用于声明"嵌套作用域的变量"(即外层函数的局部变量,非全局、非内层局部)。

python 复制代码
def g():
    count = 0

    def f():
        # 显式声明:count 是嵌套作用域(外层 g 的局部变量)
        nonlocal count
        print(count)  # 读取外层变量
        count = 1  # 修改外层变量

    f()
    print(count)  # 输出:1(外层 count 已被修改)

g()  # 执行结果:0 → 1(无报错)
  • nonlocal 只能用于嵌套作用域(内层函数引用外层函数的局部变量),不能用于全局变量;
  • 若外层作用域中无该变量,nonlocal 会报错(无法像 global 那样"创建"全局变量)。

4. 修改 vs 赋值

前面提到"全局变量可读不可写",但对全局可变对象(如字典、列表、集合),函数内修改其内容时却不会报错。这是因为"修改对象内容"与"变量赋值"是两个不同的操作。

python 复制代码
# 定义全局可变对象(字典)
d = {"a": 1}

def f():
    # 修改字典 d 的内容(不是对 d 变量赋值)
    d["a"] = 2
    print(d)

f()  # 输出:{'a': 2}(无报错,全局字典被修改)
  • 变量赋值d = {"b": 3} → 改变变量 d 的指向(让 d 指向新的字典对象),属于"对变量赋值",会触发"赋值即局部"规则;
  • 对象内容修改d["a"] = 2 → 未改变变量 d 的指向(d 仍指向原全局字典),仅修改了 d 指向的对象内部数据,不属于"对变量赋值",因此 Python 仍认为 d 是全局变量,使用 load_global 指令读取。

若对全局字典 d 直接赋值(改变指向),仍需 global 声明,否则报错:

python 复制代码
d = {"a": 1}

def f():
    # 对全局变量 d 赋值(改变指向)
    d = {"b": 3}  # 赋值操作→默认 d 是局部变量

f()  # 无报错,但全局 d 未被修改
print(d)  # 输出:{'a': 1}(全局 d 仍为原对象)

若需修改全局变量 d 的指向,需添加 global d 声明:

python 复制代码
def f():
    global d
    d = {"b": 3}

f()
print(d)  # 输出:{'b': 3}(全局 d 被修改)

5. 总结

  1. "赋值即局部" :函数/内层函数中,若变量有赋值操作(= 左侧),默认是局部变量,与赋值位置无关;
  2. global 管全局 :需在函数内修改"全局变量的指向"时,必须用 global 显式声明;
  3. nonlocal 管嵌套 :需在闭包内层修改"外层函数的局部变量"时,必须用 nonlocal 显式声明。
关键字 适用场景 作用 注意事项
global 函数内操作全局变量(需赋值时) 声明变量是"全局作用域"的,而非局部 可修改全局变量的指向(如 d = new_obj
nonlocal 闭包内层函数操作外层函数的局部变量(需赋值时) 声明变量是"外层嵌套作用域"的,非局部/非全局 不能用于全局变量;外层作用域必须存在该变量
相关推荐
xiaohanbao092 小时前
理解神经网络流程
python·神经网络
韩立学长2 小时前
【开题答辩实录分享】以《基于Python的旅游网站数据爬虫研究》为例进行答辩实录分享
python·旅游
-森屿安年-3 小时前
C++ 类与对象
开发语言·c++
小蒜学长3 小时前
springboot基于javaweb的小零食销售系统的设计与实现(代码+数据库+LW)
java·开发语言·数据库·spring boot·后端
会开花的二叉树3 小时前
c语言贪吃蛇游戏开发
c语言·开发语言
韩立学长3 小时前
【开题答辩实录分享】以《C#大型超市商品上架调配管理系统的设计与实现》为例进行答辩实录分享
开发语言·c#
小熊出擊4 小时前
【pytest】finalizer 执行顺序:FILO 原则
python·测试工具·单元测试·pytest
tao3556674 小时前
【Python刷力扣hot100】49. Group Anagrams
开发语言·python·leetcode
龙腾AI白云4 小时前
大模型-扩散模型(Diffusion Model)原理讲解(4)
开发语言