杰瑞科技汇

Python global 作用域如何正确使用?

什么是全局作用域?

全局作用域 是指在 Python 程序的最外层定义的变量、函数、类等标识符所存在的“空间”,它的特点是:

Python global 作用域如何正确使用?-图1
(图片来源网络,侵删)
  1. 生命周期:从程序开始执行时创建,直到整个程序运行结束才销毁。
  2. 可见性:默认情况下,程序内的任何地方(包括函数内部)都可以“访问”(读取)全局作用域中的变量。
  3. 定义位置:任何没有缩进,或者位于模块(.py 文件)最顶层的代码,都在全局作用域中。

一个简单的例子:

# 这里的 message 变量定义在全局作用域
message = "Hello, Global Scope!"
def greet():
    # 在函数内部,我们可以直接访问全局变量 message
    print(message)
# 调用函数
greet()
# 输出: Hello, Global Scope!

在这个例子中,message 就是一个全局变量,无论在 greet() 函数内部还是外部,我们都可以读取它的值。


关键问题:在函数内部修改全局变量

这是初学者最容易混淆的地方,记住一个核心原则:

在函数内部,如果你只是“读取”一个全局变量,可以直接使用,但如果你要“修改”一个全局变量,Python 会默认将其视为一个局部变量,除非你显式地声明它为全局变量。

Python global 作用域如何正确使用?-图2
(图片来源网络,侵删)

只读访问 (可以)

global_var = 100
def read_global():
    # 只读取,不修改
    print(f"Inside function, global_var is: {global_var}")
read_global()
print(f"Outside function, global_var is: {global_var}")
# 输出:
# Inside function, global_var is: 100
# Outside function, global_var is: 100

修改访问 (会出错,除非使用 global 关键字)

错误示范:

下面的代码会抛出 UnboundLocalError,因为 Python 看到 global_var = global_var + 1 这行代码,认为你想要在函数内部创建一个名为 global_var 的局部变量,但在执行赋值操作之前,你尝试读取这个还不存在的局部变量,所以报错。

global_var = 100
def modify_global_wrong():
    print(f"Before modification, global_var is: {global_var}") # 尝试读取
    global_var = global_var + 1  # 尝试修改
    print(f"After modification, global_var is: {global_var}")
modify_global_wrong()
# 输出:
# Before modification, global_var is: 100
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 2, in modify_global_wrong
# UnboundLocalError: local variable 'global_var' referenced before assignment

正确示范:使用 global 关键字

为了在函数内部修改全局变量,你必须使用 global 关键字来告诉 Python:“我要操作的这个 global_var 是全局作用域的那个,而不是要创建一个新的局部变量”。

global_var = 100
def modify_global_correct():
    # 声明 global_var 是全局变量
    global global_var
    print(f"Before modification, global_var is: {global_var}")
    global_var = global_var + 1  # 现在这是在修改全局变量
    print(f"After modification, global_var is: {global_var}")
modify_global_correct()
print(f"Outside function, final global_var is: {global_var}")
# 输出:
# Before modification, global_var is: 100
# After modification, global_var is: 101
# Outside function, final global_var is: 101

global 关键字的详细用法

global 关键字用于在函数内部声明一个或多个变量为全局变量。

语法:

global variable_name1, variable_name2, ...

作用:

  1. 声明变量来源:告诉解释器,这个变量是定义在全局作用域的。
  2. 允许修改:允许你在函数内部对该变量进行赋值操作,从而修改全局变量的值。
  3. 创建新全局变量:如果全局作用域中不存在该变量,使用 global 关键字并在函数中为其赋值,会创建一个新的全局变量。

示例:创建新全局变量

def create_new_global():
    # 声明并创建一个新的全局变量 new_var
    global new_var
    new_var = "I am a new global variable!"
create_new_global()
# 现在可以在全局访问 new_var
print(new_var)
# 输出: I am a new global variable!

作用域的查找顺序(LEGB 规则)

理解了全局作用域,你还需要知道 Python 是如何查找一个变量的,Python 遵循 LEGB 规则,按顺序查找:

  1. L (Local):局部作用域,即函数内部。
  2. E (Enclosing):嵌套作用域,即嵌套函数的外层函数作用域。
  3. G (Global):全局作用域,即模块的最顶层。
  4. B (Built-in):内置作用域,即 Python 解释器内置的函数和变量(如 len(), print() 等)。

图示:

B (Built-in)
    ^
    |
G (Global)  <-- my_module.py 的顶层
    ^
    |
E (Enclosing)  <-- 外层函数 outer_func()
    ^
    |
L (Local)    <-- 内层函数 inner_func()

例子:

# G: Global
global_var = "Global"
def outer_func():
    # E: Enclosing
    enclosing_var = "Enclosing"
    def inner_func():
        # L: Local
        local_var = "Local"
        # 按照 LEGB 顺序查找
        print(local_var)      # 找到 L
        print(enclosing_var)  # 找到 E
        print(global_var)    # 找到 G
        print(len)           # 找到 B (内置函数)
    inner_func()
outer_func()

全局作用域的最佳实践

虽然全局变量很方便,但过度使用它们会带来很多问题,

  • 代码难以维护:任何函数都可以修改全局变量,导致程序状态难以追踪。
  • 命名冲突:在大型项目中,不同模块可能会不小心使用相同的全局变量名。
  • 函数副作用:函数不再“纯净”,因为它会改变外部环境,使得测试和复用变得困难。

有以下几个最佳实践:

  1. 最小化全局变量的使用:只在真正需要在整个程序中共享状态时才使用全局变量。
  2. 使用 global 关键字明确意图:当你确实需要在函数中修改全局变量时,务必使用 global 关键字,这向其他开发者清晰地表明了你的意图。
  3. 优先使用函数参数和返回值:这是更“函数式”、更安全的方式,函数通过接收参数来获取数据,通过返回值来输出结果,而不是直接修改外部变量。

好的实践 (通过参数和返回值):

def add_one(number):
    # 不依赖任何全局变量,纯粹根据输入计算
    return number + 1
count = 10
new_count = add_one(count)  # 通过参数传递,通过返回值获取结果
print(new_count) # 输出: 11
print(count)    # 原始变量没有被修改

什么时候使用全局变量是合理的?

  • 配置常量API_KEY = "..."DEBUG = True,这些值在整个程序中都是不变的。
  • 程序状态管理:在一个简单的游戏中,player_score 可能是一个全局变量,由多个事件处理函数共同更新。
  • 缓存:为了避免重复计算,将计算结果存储在一个全局变量中。

特性 描述
定义 程序最顶层定义的变量、函数等的作用域。
生命周期 从程序开始到结束。
访问 默认可在任何地方读取
修改 在函数内修改必须使用 global 关键字声明。
查找规则 遵循 LEGB 规则:Local -> Enclosing -> Global -> Built-in。
最佳实践 谨慎使用,优先通过参数和返回值传递数据,以提高代码的可维护性和可测试性。
分享:
扫描分享到社交APP
上一篇
下一篇