查看全课大纲(第 29 / 51 节)
- 1.Python 是什么?为什么值得学?
- 2.如何为 Python 编程准备一个“全家桶”环境?
- 3.Python 有哪几种执行方法?
- 4.行与缩进到底有什么用?
- 5.注释有什么用,怎么写?
- 6.变量:如何给数据起个好名字?
- 7.运算符的优先级和常见坑点是什么?
- 8.Python 中,数字和布尔值究竟有哪几种?
- 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
- 10.如何将程序的结果展示给用户或保存下来?
- 11.CSV 文件怎么读怎么写才方便?
- 12.列表是什么,如何创建和访问它?
- 13.列表的修改操作,有哪些方法?
- 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
- 15.判断语句:如何让程序根据条件做不同的事?
- 16.for 循环如何遍历序列中的元素?
- 17.while 循环什么时候才会停?
- 18.while 循环的结束条件,你写对了吗?
- 19.循环中途想提前结束,怎么办?
- 20.`break` 和 `continue` 到底有什么区别?
- 21.列表推导式如何让代码更简洁高效?
- 22.字符串怎么定义、拼接和操作?
- 23.元组:为什么定义单个元素时要加逗号?
- 24.集合(set)怎么用?如何快速去重和做集合运算?
- 25.字典是什么,为什么比列表更方便?
- 26.字典的增删改查与遍历,如何避免 KeyError?
- 27.函数里的变量,为什么有的能用有的不能用?
- 28.在函数内部修改全局变量,为什么有时不生效?
- 29.函数里能修改全局变量吗?返回值怎么用?
- 30.匿名函数:如何用 lambda 快速定义简单函数?
- 31.面向对象编程是什么,为什么需要它?
- 32.类中的方法和属性到底怎么定义和使用?
- 33.如何控制对对象内部属性和方法的访问?
- 34.异常发生时,程序会怎样继续执行?
- 35.如何组织代码:包和模块
- 36.`if __name__ == '__main__'` 到底有什么用?
- 37.如何用 IDE 的 Debug 功能排查程序错误?
- 38.Matplotlib 绘图的第一步该做什么?
- 39.Figure 是什么,如何设置图形尺寸?
- 40.如何自定义 Matplotlib 的坐标轴?
- 41.如何为图表添加图例,让不同曲线一目了然?
- 42.饼图怎么画才能直观展示数据占比?
- 43.如何用 Matplotlib 绘制散点图?
- 44.柱状图怎么画?
- 45.如何使用 Matplotlib 绘制等高线图?
- 46.如何在 Matplotlib 中绘制子图?
- 47.zip 函数如何把多个列表“打包”在一起?
- 48.如何给可迭代对象加上索引?
- 49.random 模块如何生成随机数?
- 50.math 模块怎么用?
- 51.Counter 如何快速统计元素出现次数?
函数里能修改全局变量吗?返回值怎么用?
约 9 分钟
小象实战讲义 · Python 编程从入门到实践
在上一节我们学习了函数的基本定义、调用以及局部变量和全局变量的概念。本节我们将深入探讨全局变量在函数中使用的两个关键细节,并系统学习函数的返回值机制。掌握这些知识后,你将能更安全地在函数内外传递和修改数据,并让函数执行的结果能被外部代码所使用。
💡 核心导读
- 全局变量的使用时机:全局变量必须在函数调用之前定义,而非函数定义之前。
- 可变类型的特殊规则:在函数内修改列表、字典等可变全局变量的元素时,无需使用
global关键字。 - 返回值的作用:使用
return语句将函数内部的计算结果传递到外部。 - 返回多个值:可以通过列表、字典或元组一次性返回多个值。
- 接收多个返回值:可以使用多个变量直接接收元组或列表形式的返回值,并用下划线
_忽略不需要的值。
全局变量的使用时机
一个全局变量能否在函数中被使用,取决于它是否在函数被调用之前被定义。
# 示例:全局变量定义时机的影响
number1 = 100 # 在函数定义之前定义,可以使用
def print_numbers():
print("number1: {}".format(number1))
print("number2: {}".format(number2))
# print("number3: {}".format(number3)) # 取消注释会报错:NameError
number2 = 200 # 在函数定义之后,但在调用之前定义,可以使用
print_numbers() # 函数调用
# number3 = 300 # 在函数调用之后定义,此时在函数内无法使用number1: 100
number2: 200关键点:
number1在函数定义和调用前定义,函数内可以访问。number2在函数定义之后,但在print_numbers()被调用之前定义,函数内依然可以访问。number3如果在函数调用之后才定义,那么在函数内部尝试访问它时,Python 会抛出NameError,提示变量未定义。
因此,判断依据是函数调用的时刻,而非函数定义的时刻。只要在调用函数时,全局变量已经存在,就可以在函数内部使用它。
修改可变类型的全局变量
对于数字、字符串这类不可变类型的全局变量,在函数内部修改其值需要使用 global 关键字声明。但对于列表、字典这类可变类型的全局变量,如果只是修改其内部的元素(如增删列表项、修改字典键值对),则不需要使用 global 关键字。
# 示例:在函数内修改列表和字典全局变量
g_num_list = [1, 2, 3]
g_info_dict = {"name": "张三", "age": 20}
def update_info():
# 修改可变全局变量内部的元素,无需 global 声明
g_num_list.append(4) # 向列表添加元素
g_info_dict["gender"] = "男" # 向字典添加键值对
def get_info():
print("列表内容:", g_num_list)
print("字典内容:", g_info_dict)
update_info()
get_info()列表内容: [1, 2, 3, 4]
字典内容: {'name': '张三', 'age': 20, 'gender': '男'}原理:append 和 g_info_dict["gender"] = "男" 这两种操作,都是在原地修改对象本身的内容,并没有改变变量 g_num_list 和 g_info_dict 所指向的那个列表/字典对象。因此,Python 允许这种修改。
对比:如果你在函数内写了 g_num_list = [4,5,6],这相当于让局部变量 g_num_list 指向了一个全新的列表,此时就需要 global g_num_list 来声明你要修改的是全局变量。
函数的返回值
函数通过 return 语句把执行结果交给调用者,调用处可以用一个变量接收这个结果。
返回单个值与多个值
最常见的情况是返回一个计算结果;如果需要一次带出多个相关的数据,就把它们装进列表、字典或元组再返回。函数的一次 return 只能返回一个对象,这个对象可以是容器。
# 返回两数之和
def x_y_sum_return(x, y):
result = x + y
return result # 将结果返回
num1 = 10
num2 = 20
sum_result = x_y_sum_return(num1, num2)
print("%d + %d = %d" % (num1, num2, sum_result))
# 计算两个数的加、减、乘、除结果
def x_y_comp_list(x, y):
rs1 = x + y
rs2 = x - y
rs3 = x * y
rs4 = x / y
rs = [rs1, rs2, rs3, rs4] # 把四个结果放进列表
return rs
rs1 = x_y_comp_list(8, 3)
print(type(rs1)) # 查看返回值类型
print(rs1)
# 用字典装载返回值,键名可以自己定义
def x_y_comp_dict(x, y):
rs1 = x + y
rs2 = x - y
rs3 = x * y
rs4 = x / y
rs = {"加": rs1, "减": rs2, "乘": rs3, "除": rs4}
return rs
rs2 = x_y_comp_dict(8, 3)
print(type(rs2))
print(rs2)
# 用元组装载返回值
def x_y_comp_tuple(x, y):
rs1 = x + y
rs2 = x - y
rs3 = x * y
rs4 = x / y
return rs1, rs2, rs3, rs4 # 多个值用逗号分隔,默认封装成元组
rs3 = x_y_comp_tuple(8, 3)
print(type(rs3))
print(rs3)
# 返回元组或列表时,可以用多个变量按顺序接收(解包)
a, b, c, d = x_y_comp_list(8, 3)
print("加:{},减:{},乘:{},除:{}".format(a, b, c, d))
# 只关心加和乘,除的结果用下划线 _ 占位忽略
a, _, c, _ = x_y_comp_tuple(8, 3)
print("加:{},乘:{}".format(a, c))10 + 20 = 30
<class 'list'>
[11, 5, 24, 2.6666666666666665]
<class 'dict'>
{'加': 11, '减': 5, '乘': 24, '除': 2.6666666666666665}
<class 'tuple'>
(11, 5, 24, 2.6666666666666665)
加:11,减:5,乘:24,除:2.6666666666666665
加:11,乘:24- 列表:按位置存放多个结果,适合结果本身没有名字含义的场景。
- 字典:给每个结果起一个键名(如“加”“减”),返回值一眼就能看懂。
- 元组:写法最轻量,
return rs1, rs2, rs3, rs4会自动把多个值封装成元组。 - 解包:当返回值是元组或列表时,可以用多个变量按顺序接收,
a, b, c, d = x_y_comp_list(8, 3);不关心的位置用下划线_占位忽略即可。
📝 动手练一练
- 修复全局变量错误:下面的代码会报错,请指出错误原因并修改,使其能正确打印出所有全局变量的值。
total = 0 def calculate_total(): total = price * quantity print("总价: {}".format(total)) price = 25 calculate_total() quantity = 3
👉 点击查看参考答案
错误原因:函数 calculate_total() 在调用时,全局变量 quantity 还未定义,导致函数内部无法访问 quantity,从而引发 NameError。
正确写法:确保函数调用时,其内部用到的所有全局变量都已定义。
total = 0
price = 25
quantity = 3 # 将 quantity 的定义移到函数调用之前
def calculate_total():
total = price * quantity # 此时 price 和 quantity 都已存在
print("总价: {}".format(total))
calculate_total()运行结果:
总价: 75注意:函数内的 total 是局部变量,它修改的不是外部的全局变量 total。如果要修改全局变量 total,需要在函数内使用 global total 声明。
- 编写多返回值函数:编写一个函数
split_name(full_name),接收一个包含姓和名的字符串(如"张 三"),返回一个包含姓和名的元组。然后调用这个函数,并用两个变量分别接收姓和名。
👉 点击查看参考答案
思路:使用字符串的 split() 方法按空格分割全名,再把姓和名按顺序返回,调用处用两个变量解包接收。
def split_name(full_name):
"""分割姓名,返回(姓, 名)"""
name_parts = full_name.split() # 默认按空格分割
# 确保分割后有两部分,简单处理
if len(name_parts) >= 2:
return name_parts[0], name_parts[1]
else:
return name_parts[0], "" # 如果只有一部分,名称为空
last_name, first_name = split_name("张 三")
print("姓: {}, 名: {}".format(last_name, first_name))
# 测试另一个名字
last_name2, first_name2 = split_name("欧阳 峰")
print("姓: {}, 名: {}".format(last_name2, first_name2))运行结果:
姓: 张, 名: 三
姓: 欧阳, 名: 峰常见错误
| 错误写法 | 现象或报错 | 原因 | 正确写法 |
|---|---|---|---|
| 在函数调用后定义全局变量,并在函数内使用 | NameError: name 'xxx' is not defined | 函数执行时,该全局变量尚未被创建。 | 将全局变量的定义移到函数调用之前。 |
试图修改不可变全局变量(如整数)而不声明 global | 函数内的修改不影响外部变量,可能造成逻辑错误。 | 在函数内对不可变类型赋值会创建新的局部变量。 | 在函数内使用 global var_name 声明要修改的全局变量。 |
误以为 return 语句后的代码还会执行 | 函数在遇到 return 后立即结束,其后的代码被忽略。 | return 标志着函数执行的终止和结果的返回。 | 将 return 语句放在函数逻辑的最后,或使用条件分支控制返回时机。 |
解包时变量个数与返回值个数不一致,如 a, b, c = x_y_comp_tuple(8, 3)(返回 4 个值) | ValueError: too many values to unpack (expected 3) | 解包要求左右数量一致。 | 让接收变量个数与返回值个数相同,不用的位置用 _ 占位。 |
小结
- 全局变量访问:函数内部可以访问在它被调用之前已经定义好的全局变量。
- 修改可变全局变量:在函数内修改列表、字典等可变全局变量的内部元素时,无需使用
global关键字。 - 函数返回值:使用
return语句把函数内部计算的结果交给调用者。 - 返回多个值:通过将多个值放入列表、字典或元组中,可以实现“返回多个值”。
- 接收多个返回值:当函数返回元组或列表时,可以使用多个变量直接解包接收,用下划线
_忽略不关心的值。
📋 行动清单
- 将“全局变量的使用时机”部分的示例代码中的
number3定义取消注释,并取消函数内对应的print语句注释,亲自运行一下,观察报错信息。 - 尝试写一个函数,修改一个全局的列表(例如排序或反转),但不使用
global关键字,验证修改是否生效。 - 将“使用多个变量接收返回值”部分的示例,改成返回一个字典,并尝试用多个变量接收字典中的各个值(提示:需要按键取值)。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第3章代码包:函数(含1 个 Notebook)ZIP · 3KB