Python中全局变量和局部变量有什么区别

Aria
Aria
发布时间:2026-10-26 | 11人浏览过
原创

一、核心结论:一分钟看清根本区别

在 Python 中,全局变量(Global Variable)与局部变量(Local Variable)最根本的区别在于它们所属的作用域(Scope)和生命周期(Lifetime):

  • 定义位置不同:全局变量定义在函数或类的外部(模块顶层);局部变量定义在函数内部(包括函数的形参)。
  • 作用范围不同:全局变量在整个当前模块(当前 .py 文件)内处处可见,所有函数均可直接读取;局部变量只在定义它的函数内部可见,函数外部完全无法感知其存在。
  • 生命周期不同:全局变量随着模块的加载运行而创建,直到整个脚本运行结束才释放;局部变量在函数被调用执行时动态分配,一旦函数执行完毕返回,局部变量就会被立即销毁并回收内存。
  • 修改权限不同:在函数内部可以直接读取全局变量;但如果想在函数内部给全局变量重新赋值,必须先使用 global 关键字显式声明,否则 Python 会默认创建同名的全新局部变量。
Python中全局变量和局部变量有什么区别
图 1:全局变量与局部变量在作用域、生命周期及访问边界上的结构对照

二、局部变量:函数的私有数据空间

局部变量是定义在函数内部的变量,包括函数的形参。局部变量最大的特征是高内聚、强隔离。每一次调用函数时,Python 都会在调用栈中为该函数分配一个专属的栈帧(Frame)。在这个栈帧中定义的变量,对外界完全不可见。

1. 基本表现:离开函数即失效

以下是一个直观的示例:

def greet():
    message = "Hello, Python!"  # message 是 greet 函数的局部变量
    print("函数内部读取:", message)

greet()

# 尝试在外部访问局部变量
try:
    print("函数外部读取:", message)
except NameError as e:
    print("外部访问触发异常:", e)
运行结果输出示例
函数内部读取: Hello, Python!
外部访问触发异常: name 'message' is not defined

当 greet() 执行完毕返回后,该函数的栈帧被销毁,message 占用的引用随之释放。主程序外部并不存在名为 message 的变量,因此抛出 NameError。

2. 局部变量遮盖(Shadowing)

如果函数内部定义了一个与外部全局变量同名的局部变量,Python 会优先使用内部的局部变量,这种现象称为“变量遮盖”。

val = 100  # 全局变量

def demo():
    val = 20  # 局部变量,同名遮盖
    print("函数内部 val =", val)

demo()
print("函数外部 val =", val)
运行结果输出示例
函数内部 val = 20
函数外部 val = 100

函数内部给 val 赋值时,Python 只在当前函数的局部命名空间中绑定了一个新的局部变量,外部全局变量的值依然是 100,两者互不干扰。

三、全局变量:模块内的共享公告板

全局变量定义在 Python 文件的最外层代码中。它的作用域覆盖整个文件模块,模块内的任何函数都可以直接读取它的值。

SITE_NAME = "Python技术指南"  # 全局变量
USER_COUNT = 5000              # 全局变量

def print_header():
    # 直接读取全局变量,无需额外声明
    print(f"欢迎访问 {SITE_NAME},当前已有 {USER_COUNT} 位用户。")

def check_status():
    if USER_COUNT > 1000:
        print(f"[{SITE_NAME}] 系统处于高活跃状态。")

print_header()
check_status()
运行结果输出示例
欢迎访问 Python技术指南,当前已有 5000 位用户。
[Python技术指南] 系统处于高活跃状态。

全局变量适合存放模块级别的配置项、常量(如数据库连接配置、接口地址)或需要跨多个函数共享的基础元数据。

四、Python的变量查找链:LEGB 原则

为什么函数能直接读取全局变量,但外部不能读取局部变量?其背后是 Python 的作用域查找机制——LEGB 原则。

当代码中引用一个变量名时,Python 解释器会严格按照以下顺序从内向外逐层检索,只要在某一层次找到了该名字,就立即使用并停止查找:

  1. L(Local 局部作用域):当前正在执行的函数内部。
  2. E(Enclosing 嵌套作用域):外层闭包函数的作用域(常见于函数嵌套定义场景)。
  3. G(Global 全局作用域):当前 Python 文件模块顶层定义的变量。
  4. B(Built-in 内置作用域):Python 语言自带的内置函数和异常名(如 len、print、range、ValueError 等)。

如果在所有 4 个层级中都没有找到对应名称,解释器便会抛出 NameError。

Python中全局变量和局部变量有什么区别
图 2:Python 变量名解析遵循的 LEGB 四级作用域查找模型

五、跨界修改陷阱:为什么修改全局变量会报错?

理解了 LEGB 规则后,我们再回头分析开篇提到的典型报错:

count = 0

def increase():
    count += 1  # 这一句为什么会报错?
    print(count)

increase()
报错提示:
UnboundLocalError: local variable 'count' referenced before assignment

为什么会产生 UnboundLocalError?

很多初学者认为:count += 1 应该先读取全局的 count = 0,加 1 得到 1,再存回去。

然而在 Python 的底层机制中:在函数编译成字节码时,一旦发现函数体内部存在针对某个变量名的赋值操作(如 count = ... 或 count += ...),Python 就会在编译期将该变量名强制标记为“局部变量”。

由于 count 已经被认定为局部变量,执行 count += 1(等价于 count = count + 1)时,等号右侧试图读取局部变量 count 的值,但此时局部作用域内尚未给它赋值,于是就触发了“局部变量在赋值前被引用”的错误。

解决方案一:使用 global 关键字显式声明

如果确实需要在函数内部重新给全局变量绑定新值,必须在函数体第一行使用 global 关键字告诉 Python:“不要将此变量视为局部变量,所有赋值操作直接作用于全局命名空间”。

count = 0

def increase():
    global count  # 显式声明 count 指向全局变量
    count += 1
    print("函数内部修改后 count =", count)

increase()
print("函数外部最终查看 count =", count)
运行结果输出示例
函数内部修改后 count = 1
函数外部最终查看 count = 1
注意特殊情况:可变对象(如列表、字典)的原地修改无需 global
如果全局变量是一个可变对象(例如 list 或 dict),在函数内部调用它的变异方法(如 .append()、.update() 或 obj[key] = val)并不属于给变量名重新绑定,此时不需要声明 global:
user_list = []

def add_user(name):
    user_list.append(name)  # 原地修改列表内容,无需 global 声明

add_user("Alice")
print(user_list)  # 输出: ['Alice']
Python中全局变量和局部变量有什么区别
图 3:函数操作外部数据的决策路径与排查验证步骤

六、工程实践:为什么建议尽量少用全局变量?

在编写生产环境或规模较大的 Python 项目时,经验丰富的工程师往往会刻意限制甚至杜绝全局变量的使用。主要原因包括:

  1. 代码耦合度高,难以追踪修改源头:若多个函数都在通过 global 修改同一个变量,当变量的值出现异常时,你很难排查究竟是哪个函数在什么时序下修改了它。
  2. 破坏函数的纯粹性与可测试性:依赖全局变量的函数无法脱离特定上下文独立运行,编写单元测试时必须预先构造和清理全局状态,极易造成测试用例之间的互相污染。
  3. 多线程安全隐患:多线程并发访问和修改全局变量时,如果未添加互斥锁(Lock),容易出现竞态条件(Race Condition),导致数据计算结果失真。

推荐的重构方案:参数传入与返回值传出

与其在函数内部使用 global 硬性修改,更优秀的做法是通过参数和返回值传递状态,保持函数的独立解耦:

# 传统使用 global 的写法(紧耦合)
count = 0
def increment_global():
    global count
    count += 1

# 推荐的现代函数式解耦写法(纯函数)
def increment(current_value, step=1):
    return current_value + step

counter = 0
counter = increment(counter)  # 状态在外部显式流转
print("当前计数器值:", counter)
运行结果输出示例
当前计数器值: 1

七、全维度对比总结

为了方便查阅与记忆,下表将全局变量与局部变量的所有核心维度整理汇总:

Python中全局变量和局部变量有什么区别
图 4:Python 全局变量与局部变量核心对比矩阵

八、总结与避坑自查

总结来说,全局变量和局部变量的差异可以概括为三句话:

  • 局部变量私有且短暂:它是函数运行时的内部临时工作空间,外界碰不到,用完就释放。
  • 全局变量共享且常驻:它是整个模块文件的共享公告板,任何函数都能随时看,但改写时需要特别谨慎。
  • 只读直接用,修改加 global,重构用返回值:如果只是读,直接写名字;如果是简单脚本非要改,加上 global;如果是严肃工程,优先通过参数传进、通过 return 传出。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表