当前位置: 首页 > news >正文

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的变量作用域规则,重点区分全局变量(函数外定义,全程序可见)和局部变量(函数内定义,仅函数内可见),剖析“局部变量修改全局变量”的常见错误,并详解globalnonlocal两个关键字的用法,最终通过实战案例展示如何在复杂场景中正确管理变量作用域。

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局部变量,但此时在赋值前又使用了countcount + 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返回内层函数countercounter通过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 作用域相关错误的排查技巧

  1. UnboundLocalError

    • 原因:函数内修改了未声明的全局变量,或使用了未定义的局部变量;
    • 解决:若修改全局变量,添加global声明;若用局部变量,确保赋值前已定义。
  2. 局部变量遮蔽全局变量

    • 现象:函数内使用变量时,预期是全局变量,实际用了同名局部变量;
    • 解决:避免变量名冲突,或用global明确指定全局变量。
  3. nonlocal找不到外层变量

    • 原因:nonlocal声明的变量在所有外层嵌套作用域中都不存在;
    • 解决:确保外层函数定义了该变量,或改用全局变量。

总结:变量作用域的核心规则

理解变量作用域是写出清晰、可维护代码的基础,核心要点总结如下:

  1. 作用域分类

    • 全局变量:函数外定义,全程序可见,生命周期与程序一致;
    • 局部变量:函数内定义,仅函数内可见,函数结束后销毁;
    • 嵌套作用域:外层函数的局部变量,可被内层函数读取(需nonlocal修改)。
  2. 关键字用法

    • global:在函数内声明变量为全局变量,允许修改全局变量;
    • nonlocal:在嵌套函数内声明变量为外层函数的局部变量,允许修改该变量。
  3. 最佳实践

    • 优先使用局部变量和函数参数/返回值,减少全局变量依赖;
    • 必须使用全局变量时,用大写命名(如GLOBAL_CONFIG),明确标识;
    • 嵌套函数中需共享状态时,用nonlocal(而非全局变量),避免污染全局作用域。

掌握这些规则,你就能避免90%以上的作用域相关错误,写出逻辑更清晰、更易维护的Python代码。记住:好的变量作用域设计,能让代码的“数据流”一目了然。


文章转载自:

http://pzdDTaB8.rqwmt.cn
http://CdQqqdCy.rqwmt.cn
http://IPknx1qr.rqwmt.cn
http://zPHAlW9L.rqwmt.cn
http://NZtb4RhK.rqwmt.cn
http://QY09tGev.rqwmt.cn
http://qGpqqCai.rqwmt.cn
http://7XSReswH.rqwmt.cn
http://i7kvdB5O.rqwmt.cn
http://FMLdytq7.rqwmt.cn
http://ngeQIiJ5.rqwmt.cn
http://bpaONy2y.rqwmt.cn
http://K9JX6QYh.rqwmt.cn
http://UQRLd2Fy.rqwmt.cn
http://pOCbz0hK.rqwmt.cn
http://FGlA6MkR.rqwmt.cn
http://lyFLbFfe.rqwmt.cn
http://ROaAmpRc.rqwmt.cn
http://cXV8BMUA.rqwmt.cn
http://SM8CcaM5.rqwmt.cn
http://NgHSwDfF.rqwmt.cn
http://JxM4R15C.rqwmt.cn
http://uymv3S7P.rqwmt.cn
http://b4itycKV.rqwmt.cn
http://6zujzYt0.rqwmt.cn
http://2X7vEtwS.rqwmt.cn
http://d1GL78OD.rqwmt.cn
http://DneNIoMM.rqwmt.cn
http://Tm40v0g3.rqwmt.cn
http://ndvhFady.rqwmt.cn
http://www.dtcms.com/a/372829.html

相关文章:

  • 深度学习(二):神经元与神经网络
  • 如何在不同 iOS 设备上测试和上架 uni-app 应用 实战全流程解析
  • iOS 开发全流程实战 基于 uni-app 的 iOS 应用开发、打包、测试与上架流程详解
  • [论文阅读] 人工智能 + 软件工程 | 大模型破局跨平台测试!LLMRR让iOS/安卓/鸿蒙脚本无缝迁移
  • 汇编基础1
  • CSS @scope与12个降低css冲突方法
  • pytorch 中是如何实现embeding 的
  • 【.Net技术栈梳理】02-核心框架与运行时(GC管理)
  • 洗完头后根据个人需求选择合适的自然风干 | 电吹风 (在保护发质的同时,也能兼顾到生活的便利和舒适。)
  • 人才教育导向下:老年生活照护实训室助力提升学生老年照护服务能力
  • Typescript入门-类型断言讲解
  • 使用Pycharm进行远程ssh(以Featurize为例)
  • 云原生:微服务与Serverless指南
  • 时序数据库选型指南:大数据与物联网时代下的深度剖析与 Apache IoTDB 实践
  • Python 实现 HTML 转 Word 和 PDF
  • 亚马逊长尾关键词怎么找?从传统运营到DeepBI的智能策略演进
  • 打造高效Jenkins CICD环境全解析
  • 学习笔记:MYSQL(4)
  • Vue的响应式底层原理:Proxy vs defineProperty
  • Jenkins运维之路(初识流水线)
  • 内窥镜冷光源
  • Linux设备内存不足如何处理
  • 【JavaSE】复习总结
  • uview使用u-popup组件当开启遮罩层禁止下层页面滚动。uniapp弹框禁止页面上下滚动。
  • 爱普生喷墨打印机所有指示灯同时闪烁,不工作,怎么解决?
  • 这是一款没有任何限制的免费远程手机控制手机的软件
  • 【LCA 树上倍增】P9245 [蓝桥杯 2023 省 B] 景区导游|普及+
  • 【计算机网络】计算机网络英文名词速查
  • C++之queue类的代码及其逻辑详解
  • 36.Java序列化与反序列化是什么