那天,我的Python函数死活改不了全局变量

免费编程软件「python+pycharm」 链接:pan.quark.cn/s/48a86be2f...

一个让人抓狂的下午

小李刚学Python不久,接了个小任务:写个程序统计用户输入的数字,并且实时显示当前的总和。

他很快写出了这样的代码:

python 复制代码
total = 0  # 全局总和

def add_number(n):
    total = total + n
    print(f"加了{n},现在总和是{total}")

add_number(5)

运行一下,报错了:

bash 复制代码
UnboundLocalError: local variable 'total' referenced before assignment

小李懵了。total明明在上面定义好了,为什么说它没定义?

他试了另一个办法------在函数里把total打印出来看看:

bash 复制代码
total = 100

def test():
    print(total)

test()  # 输出100

咦,这又能读到。那为什么刚才不能修改?

这就是Python新手最容易踩的坑之一:在函数里读全局变量OK,写就不行

今天我们就用三个场景,把这个问题彻底说清楚。


场景一:只读不写------直接使用全局变量

先弄清楚刚才那个能正常工作的例子。

scss 复制代码
count = 10

def show():
    print(count)

show()  # 10

为什么这个不报错?

因为Python在函数里找变量时,遵循一个简单的顺序:先看函数内部有没有定义,没有就往外层找

show()函数里,print(count)这句话,Python会问:

  1. 函数内部有没有一个叫count的局部变量?没有。
  2. 那上一层(全局作用域)有没有?有,值是10。

OK,拿来用。

这就像你在家里找充电器:先看看自己包里有没有,没有就去客厅找。找到了就用,没问题。

规则一:当你只是读取一个变量的值,而不给它赋值时,Python会自动向上查找,找到全局变量就用。

适用范围很广:打印日志、读取配置常量、使用全局计数器(只读)等。

但是注意------这个"不赋值"是关键。哪怕你在同一行代码里既读又写,情况就变了。


场景二:要修改------用global声明

回到小李报错的那个例子:

ini 复制代码
total = 0

def add_number(n):
    total = total + n

这里有一个total = ...,这对Python来说意味着:我要在函数内部创建一个局部变量叫total

但等号右边又用到了total,这时局部变量total还没创建好呢(赋值还没完成),结果就是"在定义前就使用了"。

Python的规则是:如果在函数内对变量进行赋值,该变量默认被视为局部变量

解决方案:明确告诉Python,"这个变量不是局部的,我要用全局那个"。

python 复制代码
total = 0

def add_number(n):
    global total
    total = total + n

add_number(5)
print(total)  # 5
add_number(3)
print(total)  # 8

加了global total之后,函数里的total就不是局部变量了,而是指向全局那个。

你可以把global理解为声明 :"接下来的total,就是外面那个,别给我新建局部的。"

规则二:函数内部要修改全局变量,必须先用global语句声明。

几点要注意:

  • global声明要放在使用变量之前,通常放在函数开头。
  • 可以同时声明多个变量:global a, b, c
  • 如果只读不写,不需要global;一旦要改,就必须加。

有人会问:那如果全局变量是列表、字典,我修改它的内容(比如append),需要global吗?

这是个好问题。看代码:

scss 复制代码
my_list = [1, 2, 3]

def add_item():
    my_list.append(4)  # 没有global

add_item()
print(my_list)  # [1, 2, 3, 4]

竟然没报错?

原因在于:append是在修改对象的内容 ,而不是给变量重新赋值 。变量my_list始终指向同一个列表对象,我们没有做my_list = ...这种重新绑定的操作。

同理:

ini 复制代码
my_dict = {"a": 1}

def update():
    my_dict["b"] = 2  # OK,不需要global

def reassign():
    my_dict = {"c": 3}  # 需要global,因为这是重新赋值

记住这个区分:改内容 vs 改指向 。改内容不用global,改指向(赋值)就要用。


场景三:跨文件共享------用模块级变量

上面两个场景都在同一个文件里。但真实项目通常会拆成多个模块(多个.py文件)。

假如你有两个文件:

config.py

ini 复制代码
# 全局配置
app_name = "我的应用"
version = "1.0"
user_count = 0

main.py

python 复制代码
import config

def increment_user():
    config.user_count += 1

def show_info():
    print(f"{config.app_name} v{config.version},用户数:{config.user_count}")

increment_user()
show_info()

这里用了config.user_count而不是直接user_count

为什么这样就能改?原因不是global,而是通过模块名去访问变量

当你写config.user_count = ...时,Python知道你是在修改config模块里的属性,不会把它当成局部变量。模块对象像个"容器",你明确告诉了Python去改容器里的东西。

这种方式的好处很明显:

  • 不需要在每个函数里写global声明
  • 多个模块可以共享同一份配置
  • 代码更清晰,一看就知道变量来自哪里

实际项目中,你可以单独建一个globals.py或者state.py,把所有需要共享的变量集中管理:

ini 复制代码
# state.py
is_logged_in = False
current_user = None
score = 0

其他模块统一用import state来读写。

规则三:跨文件共享全局状态,用模块级变量,通过模块名访问。


深入一点:为什么Python要这样设计?

你可能觉得:"别的语言全局变量随便改,Python怎么这么麻烦?"

这是有意为之的。Python的设计哲学有一条:显式优于隐式

如果一个函数能随便修改全局变量,你看到一行count = count + 1,很难判断这个count是局部还是全局。大型项目里,这种不确定性会导致极其难追踪的bug。

Python逼你写global,相当于让你明确表态:"我知道我在改全局变量,我负责。"

同样,通过模块名访问,也是在增加可读性。读代码的人看到state.score,马上知道这个score来自外部的共享状态。


三个技巧帮你少踩坑

技巧1:尽量少用全局变量

这不是说教,是真心建议。全局变量多了,程序就像一堆乱线缠在一起。函数调用的顺序会影响全局变量的值,调试起来非常痛苦。

更好的做法:把需要共享的状态封装成对象,或者通过参数传递、返回值接收。

对比一下:

python 复制代码
# 不推荐:到处改全局
total = 0

def add(x):
    global total
    total += x

def subtract(x):
    global total
    total -= x
ruby 复制代码
# 推荐:用类封装
class Calculator:
    def __init__(self):
        self.total = 0
    
    def add(self, x):
        self.total += x
    
    def subtract(self, x):
        self.total -= x

或者更简单的函数式风格:

ini 复制代码
def add(total, x):
    return total + x

total = 0
total = add(total, 5)

技巧2:用global之前,先问问自己能不能用返回值替代

很多情况下,你并不需要改全局变量。让函数返回新值,调用方去更新全局变量,逻辑更清晰。

ini 复制代码
# 不推荐
counter = 0
def increment():
    global counter
    counter += 1

# 推荐
counter = 0
def increment(c):
    return c + 1
counter = increment(counter)

技巧3:在函数开头集中声明所有用到的全局变量

如果确实需要用global,把所有声明放在函数顶部,一目了然:

python 复制代码
def complex_operation():
    global total, count, status
    # 清晰的声明
    # 下面再用这些变量

不要写到一半突然冒出一个global,容易乱。


总结对比表

场景 做法 是否需要global 举例
函数内只读取全局变量 直接用 print(total)
函数内修改全局变量(重新赋值) 先用global声明 global total; total = 10
修改全局可变对象的内容 直接用 my_list.append(1)
跨文件访问共享变量 import模块后,通过模块名访问 config.user_count = 10

一个小练习帮你巩固

下面这段代码有几处错误?试着找出来并改正。

ini 复制代码
score = 100
history = []

def update_score(new_score):
    score = new_score  # 意图是修改全局score

def add_record(record):
    history.append(record)

def reset():
    score = 0
    history = []

update_score(90)
add_record("update")
reset()
print(score)  # 期望输出0,实际是多少?

答案:

  1. update_score里的score = new_score创建了局部变量,没改到全局的。需要加global score
  2. reset里,score = 0也是局部,同样需要global score
  3. resethistory = []创建了新的局部变量,外面的history没变。这里如果想要清空列表,应该用history.clear()而不是重新赋值。如果要重新赋值,需要global history

修正后的版本:

python 复制代码
score = 100
history = []

def update_score(new_score):
    global score
    score = new_score

def add_record(record):
    history.append(record)  # 改内容,不用global

def reset():
    global score
    score = 0
    history.clear()  # 清空内容,不用global

update_score(90)   # score变成90
add_record("update")
reset()            # score变成0,history清空
print(score)       # 0

最后说几句

全局变量不是洪水猛兽,小脚本、快速原型、配置类变量用起来很方便。但随着代码增长,函数和全局变量的耦合会越来越紧。

记住一句话:函数最好只依赖它的参数,只通过返回值影响外部。这样的函数容易测试、容易复用、容易理解。

当你下次在函数里想写global的时候,停一下,想一想:有没有办法用参数和返回值来实现?

如果没有,那就放心用。Python给了你这个工具,合理使用就行。

小李后来学明白了这个知识点,再也没被UnboundLocalError困住过。希望你也是。

相关推荐
右耳朵猫AI2 小时前
Python周刊2026W22 | Django 6.1 Alpha 1发布、Nuitka 4.1发布、PEP 831终稿、PEP 808已接受
开发语言·python·django
Wonderful U2 小时前
Python+Django实战|美食菜谱分享与食材采购一体化系统:食谱发布收藏、图文教程、食材商城、购物车、订单管理、美食点评、智能食谱推荐
python·django·美食
秦jh_2 小时前
【LangChain核心组件】少样本提示(示例选择器)
人工智能·python·langchain
资深流水灯工程师2 小时前
PyCharm 增强插件完整安装与配置指南(PySide6 开发专用)
ide·python·pycharm
Kobebryant-Manba2 小时前
学习模型构造
python·深度学习·学习
天天进步20152 小时前
Python全栈项目--基于Python的数据库管理工具
开发语言·数据库·python
阿提说说2 小时前
我的 NVIDIA 考试攻略
python·大模型·agent
xyz_CDragon3 小时前
OpenClaw 局域网调用 Ollama 本地大模型:完整配置与踩坑指南
python·ai编程·集成学习·ollama·deepseek·openclaw
极光代码工作室3 小时前
基于NLP的论文关键词提取系统
python·深度学习·自然语言处理·nlp