查看全课大纲(第 29 / 51 节)
  1. 1.Python 是什么?为什么值得学?
  2. 2.如何为 Python 编程准备一个“全家桶”环境?
  3. 3.Python 有哪几种执行方法?
  4. 4.行与缩进到底有什么用?
  5. 5.注释有什么用,怎么写?
  6. 6.变量:如何给数据起个好名字?
  7. 7.运算符的优先级和常见坑点是什么?
  8. 8.Python 中,数字和布尔值究竟有哪几种?
  9. 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
  10. 10.如何将程序的结果展示给用户或保存下来?
  11. 11.CSV 文件怎么读怎么写才方便?
  12. 12.列表是什么,如何创建和访问它?
  13. 13.列表的修改操作,有哪些方法?
  14. 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
  15. 15.判断语句:如何让程序根据条件做不同的事?
  16. 16.for 循环如何遍历序列中的元素?
  17. 17.while 循环什么时候才会停?
  18. 18.while 循环的结束条件,你写对了吗?
  19. 19.循环中途想提前结束,怎么办?
  20. 20.`break` 和 `continue` 到底有什么区别?
  21. 21.列表推导式如何让代码更简洁高效?
  22. 22.字符串怎么定义、拼接和操作?
  23. 23.元组:为什么定义单个元素时要加逗号?
  24. 24.集合(set)怎么用?如何快速去重和做集合运算?
  25. 25.字典是什么,为什么比列表更方便?
  26. 26.字典的增删改查与遍历,如何避免 KeyError?
  27. 27.函数里的变量,为什么有的能用有的不能用?
  28. 28.在函数内部修改全局变量,为什么有时不生效?
  29. 29.函数里能修改全局变量吗?返回值怎么用?
  30. 30.匿名函数:如何用 lambda 快速定义简单函数?
  31. 31.面向对象编程是什么,为什么需要它?
  32. 32.类中的方法和属性到底怎么定义和使用?
  33. 33.如何控制对对象内部属性和方法的访问?
  34. 34.异常发生时,程序会怎样继续执行?
  35. 35.如何组织代码:包和模块
  36. 36.`if __name__ == '__main__'` 到底有什么用?
  37. 37.如何用 IDE 的 Debug 功能排查程序错误?
  38. 38.Matplotlib 绘图的第一步该做什么?
  39. 39.Figure 是什么,如何设置图形尺寸?
  40. 40.如何自定义 Matplotlib 的坐标轴?
  41. 41.如何为图表添加图例,让不同曲线一目了然?
  42. 42.饼图怎么画才能直观展示数据占比?
  43. 43.如何用 Matplotlib 绘制散点图?
  44. 44.柱状图怎么画?
  45. 45.如何使用 Matplotlib 绘制等高线图?
  46. 46.如何在 Matplotlib 中绘制子图?
  47. 47.zip 函数如何把多个列表“打包”在一起?
  48. 48.如何给可迭代对象加上索引?
  49. 49.random 模块如何生成随机数?
  50. 50.math 模块怎么用?
  51. 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);不关心的位置用下划线 _ 占位忽略即可。

📝 动手练一练

  1. 修复全局变量错误:下面的代码会报错,请指出错误原因并修改,使其能正确打印出所有全局变量的值。
    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 声明。

  1. 编写多返回值函数:编写一个函数 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)解包要求左右数量一致。让接收变量个数与返回值个数相同,不用的位置用 _ 占位。

小结

  1. 全局变量访问:函数内部可以访问在它被调用之前已经定义好的全局变量。
  2. 修改可变全局变量:在函数内修改列表、字典等可变全局变量的内部元素时,无需使用 global 关键字。
  3. 函数返回值:使用 return 语句把函数内部计算的结果交给调用者。
  4. 返回多个值:通过将多个值放入列表、字典或元组中,可以实现“返回多个值”。
  5. 接收多个返回值:当函数返回元组或列表时,可以使用多个变量直接解包接收,用下划线 _ 忽略不关心的值。

📋 行动清单

  • 将“全局变量的使用时机”部分的示例代码中的 number3 定义取消注释,并取消函数内对应的 print 语句注释,亲自运行一下,观察报错信息。
  • 尝试写一个函数,修改一个全局的列表(例如排序或反转),但不使用 global 关键字,验证修改是否生效。
  • 将“使用多个变量接收返回值”部分的示例,改成返回一个字典,并尝试用多个变量接收字典中的各个值(提示:需要按键取值)。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第3章代码包:函数(含1 个 Notebook)
    ZIP · 3KB
    下载