Python快速入门专业版(十三):Python变量进阶:全局变量与局部变量(含global关键字用法)
目录
- 引言:为什么变量“看得见”或“看不见”?
- 1.变量作用域:全局变量与局部变量的核心区别
- 1.1 全局变量(Global Variable)
- 1.2 局部变量(Local Variable)
- 1.3 同名变量:局部变量“遮蔽”全局变量
- 2.常见错误:局部变量修改全局变量的“陷阱”
- 2.1 错误演示:未声明时修改全局变量
- 2.2 为什么“读取”可以,“修改”不行?
- 3.global关键字:在函数内操作全局变量
- 3.1 global关键字的基础用法
- 3.2 用global声明未定义的全局变量
- 3.3 global的注意事项
- 4.nonlocal关键字:嵌套函数中修改外层变量
- 4.1 嵌套函数的作用域问题
- 4.2 nonlocal关键字的用法
- 4.3 实战案例:用nonlocal实现计数器
- 5.全局变量vs局部变量:最佳实践与避坑指南
- 5.1 何时使用全局变量?
- 5.2 为什么要避免过度使用全局变量?
- 5.3 作用域相关错误的排查技巧
- 总结:变量作用域的核心规则
引言:为什么变量“看得见”或“看不见”?
在Python中,变量并非在任何地方都能被访问——一个变量能被使用的范围(即“作用域”)由它的定义位置决定。这种“作用域规则”是编程的基础逻辑,却也是新手容易混淆的点:为什么在函数内定义的变量,在函数外无法打印?为什么试图在函数内修改全局变量会报错?
本文将系统讲解Python的变量作用域规则,重点区分全局变量(函数外定义,全程序可见)和局部变量(函数内定义,仅函数内可见),剖析“局部变量修改全局变量”的常见错误,并详解global
和nonlocal
两个关键字的用法,最终通过实战案例展示如何在复杂场景中正确管理变量作用域。
1.变量作用域:全局变量与局部变量的核心区别
变量的“作用域”(Scope)指的是变量能被访问的代码范围。根据定义位置的不同,Python变量可分为两类:全局变量和局部变量,它们的可见性和生命周期完全不同。
1.1 全局变量(Global Variable)
定义:在函数外部定义的变量,作用域是整个程序(包括所有函数内部,除非被局部变量遮蔽)。
特性:
- 从定义位置到程序结束都有效;
- 函数内部可以直接读取全局变量的值(无需特殊声明);
- 全局变量属于全局命名空间(Global Namespace)。
代码示例:
# 定义全局变量(函数外)
global_var = "我是全局变量"def print_global():# 函数内部可以直接读取全局变量print(global_var) # 输出:我是全局变量# 调用函数,验证全局变量可访问
print_global()# 函数外部也可直接访问全局变量
print(global_var) # 输出:我是全局变量
1.2 局部变量(Local Variable)
定义:在函数内部定义的变量,作用域是当前函数内部(函数外部无法访问)。
特性:
- 仅在函数被调用时创建,函数执行结束后自动销毁;
- 函数外部无法访问局部变量(会报
NameError
); - 局部变量属于局部命名空间(Local Namespace)。
代码示例:
def create_local():# 定义局部变量(函数内)local_var = "我是局部变量"print(local_var) # 输出:我是局部变量(函数内可访问)# 调用函数,执行局部变量相关操作
create_local()# 尝试在函数外访问局部变量(报错)
try:print(local_var)
except NameError as e:print(f"错误:{e}") # 输出:错误:name 'local_var' is not defined
1.3 同名变量:局部变量“遮蔽”全局变量
当函数内部定义的局部变量与全局变量同名时,函数内部会优先使用局部变量(局部变量“遮蔽”了全局变量),这是避免全局变量被意外修改的保护机制。
代码示例:
# 全局变量
num = 100def test_scope():# 定义与全局变量同名的局部变量num = 200print(f"函数内的num(局部变量):{num}") # 输出:函数内的num(局部变量):200test_scope()# 函数外的全局变量未被修改
print(f"函数外的num(全局变量):{num}") # 输出:函数外的num(全局变量):100
原理:Python在查找变量时遵循“LEGB规则”(Local→Enclosing→Global→Built-in),即优先在当前局部作用域查找,再找外层嵌套作用域,然后是全局作用域,最后是内置作用域。
2.常见错误:局部变量修改全局变量的“陷阱”
函数内部可以读取全局变量,但直接修改全局变量会触发UnboundLocalError
——这是新手最容易遇到的作用域错误之一。
2.1 错误演示:未声明时修改全局变量
# 全局变量
count = 0def increment():# 尝试直接修改全局变量(错误)count = count + 1 # 报错:UnboundLocalError: cannot assign to local variable 'count' where it is not declared as nonlocal or globalprint(f"修改后的count:{count}")increment()
错误原因:当Python解释器在函数内部看到count = ...
这样的赋值语句时,会默认count
是局部变量,但此时在赋值前又使用了count
(count + 1
),而局部变量count
尚未定义,因此报错。
2.2 为什么“读取”可以,“修改”不行?
- 读取全局变量:函数内部没有同名局部变量时,Python会自动到全局作用域查找,因此可以正常读取;
- 修改全局变量:赋值操作会让Python将变量视为局部变量,若未提前声明,就会出现“使用未定义的局部变量”的错误。
反例(仅读取全局变量,不修改):
count = 0def show_count():# 仅读取全局变量(不修改),正常运行print(f"当前count:{count}") # 输出:当前count:0show_count() # 正常执行,无错误
3.global关键字:在函数内操作全局变量
要在函数内部修改全局变量,必须用global
关键字显式声明该变量是全局变量。global
的作用是告诉Python:“这个变量不是局部变量,而是全局变量,请去全局作用域查找并修改它”。
3.1 global关键字的基础用法
语法:在函数内部,变量赋值前添加global 变量名
。
代码示例:
# 全局变量
count = 0def increment():# 声明count是全局变量global count# 现在可以修改全局变量了count = count + 1print(f"函数内修改后的count:{count}") # 输出:函数内修改后的count:1increment()# 验证全局变量确实被修改
print(f"函数外的count:{count}") # 输出:函数外的count:1
3.2 用global声明未定义的全局变量
global
还可以声明一个“尚未定义的全局变量”,函数执行后会在全局作用域创建该变量。
def create_global():# 声明并定义一个新的全局变量global new_globalnew_global = "我是用global创建的全局变量"# 调用函数前,new_global不存在
try:print(new_global)
except NameError as e:print(f"调用前:{e}") # 输出:调用前:name 'new_global' is not defined# 调用函数,创建全局变量
create_global()# 调用后,全局变量可用
print(f"调用后:{new_global}") # 输出:调用后:我是用global创建的全局变量
3.3 global的注意事项
- 仅修改时需要声明:若函数内仅读取全局变量,无需用
global
;只有修改时才需要; - 避免滥用:过度使用全局变量会导致代码逻辑混乱(变量可被任意修改),优先用函数参数和返回值传递数据;
- 多个全局变量:可在一个
global
语句中声明多个变量,用逗号分隔,如global a, b, c
。
4.nonlocal关键字:嵌套函数中修改外层变量
在嵌套函数(函数内部定义的函数)中,若要修改外层函数的局部变量(非全局变量),需要用nonlocal
关键字。它的作用类似global
,但针对的是“外层嵌套作用域”的变量,而非全局作用域。
4.1 嵌套函数的作用域问题
外层函数的局部变量,在内层函数中默认是只读的,直接修改会报错:
def outer():# 外层函数的局部变量outer_var = "外层变量"def inner():# 尝试修改外层函数的局部变量(错误)outer_var = "修改后的外层变量" # 实际创建了一个内层局部变量print(f"inner内的outer_var:{outer_var}")inner()# 外层变量未被修改(inner内的是新局部变量)print(f"outer内的outer_var:{outer_var}")outer()
# 输出:
# inner内的outer_var:修改后的外层变量
# outer内的outer_var:外层变量
问题:内层函数的outer_var = ...
创建了一个新的局部变量(属于inner),而非修改外层函数的outer_var
。
4.2 nonlocal关键字的用法
nonlocal
声明变量是“外层嵌套作用域”的变量,允许内层函数修改它:
def outer():outer_var = "外层变量"def inner():# 声明outer_var是外层嵌套作用域的变量nonlocal outer_var# 修改外层变量outer_var = "修改后的外层变量"print(f"inner内的outer_var:{outer_var}")inner()# 外层变量已被修改print(f"outer内的outer_var:{outer_var}")outer()
# 输出:
# inner内的outer_var:修改后的外层变量
# outer内的outer_var:修改后的外层变量
注意:nonlocal
找的是“最近的外层非全局作用域”,若外层没有该变量,会报错SyntaxError
。
4.3 实战案例:用nonlocal实现计数器
nonlocal
常用于需要“保存状态”的嵌套函数,例如实现一个可累计的计数器:
def make_counter():# 外层函数的局部变量:用于保存计数状态count = 0def counter():# 声明count是外层的局部变量nonlocal countcount += 1return count# 返回内层函数(闭包)return counter# 创建计数器实例
counter1 = make_counter()
counter2 = make_counter() # 独立的计数器,状态不共享# 调用计数器,验证状态累计
print(counter1()) # 1
print(counter1()) # 2
print(counter1()) # 3print(counter2()) # 1(独立计数)
print(counter2()) # 2
原理:外层函数make_counter
返回内层函数counter
,counter
通过nonlocal
修改外层的count
,从而实现状态保存(闭包特性)。每个counter
实例拥有独立的count
,互不干扰。
5.全局变量vs局部变量:最佳实践与避坑指南
5.1 何时使用全局变量?
全局变量并非“洪水猛兽”,以下场景适合使用:
- 存储程序的全局配置(如日志级别、默认路径);
- 保存整个程序生命周期中都需要的数据(如用户登录状态)。
示例:全局配置变量
# 全局配置(程序启动时初始化)
GLOBAL_CONFIG = {"debug": False,"log_path": "./app.log"
}def set_debug(debug_mode):# 修改全局配置global GLOBAL_CONFIGGLOBAL_CONFIG["debug"] = debug_modedef get_log_path():# 读取全局配置return GLOBAL_CONFIG["log_path"]
5.2 为什么要避免过度使用全局变量?
- 可读性差:全局变量可被程序任何位置修改,难以追踪修改记录;
- 可维护性低:函数依赖全局变量时,复用和测试变得困难;
- 冲突风险:同名变量可能导致意外遮蔽(如局部变量覆盖全局变量)。
替代方案:用函数参数传递输入,用返回值传递输出,避免依赖全局变量:
# 不推荐:依赖全局变量
total = 0
def add(a, b):global totaltotal = a + b# 推荐:用参数和返回值
def add(a, b):return a + btotal = add(3, 5) # 显式赋值,逻辑清晰
5.3 作用域相关错误的排查技巧
-
UnboundLocalError:
- 原因:函数内修改了未声明的全局变量,或使用了未定义的局部变量;
- 解决:若修改全局变量,添加
global
声明;若用局部变量,确保赋值前已定义。
-
局部变量遮蔽全局变量:
- 现象:函数内使用变量时,预期是全局变量,实际用了同名局部变量;
- 解决:避免变量名冲突,或用
global
明确指定全局变量。
-
nonlocal找不到外层变量:
- 原因:
nonlocal
声明的变量在所有外层嵌套作用域中都不存在; - 解决:确保外层函数定义了该变量,或改用全局变量。
- 原因:
总结:变量作用域的核心规则
理解变量作用域是写出清晰、可维护代码的基础,核心要点总结如下:
-
作用域分类:
- 全局变量:函数外定义,全程序可见,生命周期与程序一致;
- 局部变量:函数内定义,仅函数内可见,函数结束后销毁;
- 嵌套作用域:外层函数的局部变量,可被内层函数读取(需
nonlocal
修改)。
-
关键字用法:
global
:在函数内声明变量为全局变量,允许修改全局变量;nonlocal
:在嵌套函数内声明变量为外层函数的局部变量,允许修改该变量。
-
最佳实践:
- 优先使用局部变量和函数参数/返回值,减少全局变量依赖;
- 必须使用全局变量时,用大写命名(如
GLOBAL_CONFIG
),明确标识; - 嵌套函数中需共享状态时,用
nonlocal
(而非全局变量),避免污染全局作用域。
掌握这些规则,你就能避免90%以上的作用域相关错误,写出逻辑更清晰、更易维护的Python代码。记住:好的变量作用域设计,能让代码的“数据流”一目了然。