查看全课大纲(第 27 / 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 如何快速统计元素出现次数?

函数里的变量,为什么有的能用有的不能用?

约 21 分钟

小象实战讲义 · Python 编程从入门到实践

函数是 Python 中组织代码、实现复用的核心工具。在学完列表、元组、集合、字典这些数据结构之后,本节我们将系统地学习如何定义和使用函数,并理解函数内外变量的作用范围。学完本节,你将能清晰地分辨函数参数的类型,并避免因变量作用域不清而导致的常见错误。

💡 核心导读

  • 函数定义与调用:掌握 def 关键字定义函数,以及通过函数名加括号进行调用的基本形式。
  • 形参与实参:理解定义函数时的形式参数(形参)和调用函数时传入的实际参数(实参)的区别。
  • 参数的高级用法:学习缺省参数和命名参数,让函数调用更灵活。
  • 变量作用域:分清局部变量和全局变量,知道它们各自在哪里有效,以及修改全局变量时需要 global 声明。

函数的基本形式

函数是对一段特定功能的代码封装。定义后,通过一个名字即可反复调用这段代码,无需重复编写。

定义语法:

def 函数名(参数):
    函数体代码
    return 返回值  # 可选
  • def 是定义函数的关键字。
  • 函数名 遵循变量命名规则。
  • 参数 是可选的,写在括号内,多个参数用逗号分隔。
  • 函数体是一个缩进的代码块,与 if、for 的代码块格式要求一致。
  • return 语句用于把结果返回给调用者;return 也可以没有,这种函数称为无返回值函数。

调用方式:函数名(参数)

下面是本节的第一组示例:先定义一个打印固定用户信息的无参函数,再定义一个带参函数,对比“写死信息”和“由调用者传入信息”两种做法。

# 定义无参函数
def print_user_info():
    print("name: zhangsan")
    print("age: 10")
    print("gender: male")

# 调用函数
print_user_info()

# 定义带参函数:name, age, gender 是形参
def print_user_info2(name, age, gender):
    print("name: {}".format(name))
    print("age: {}".format(age))
    print("gender: {}".format(gender))

# 调用函数,把变量作为实参传入
name = "悟空"
age = 100
gender = "female"
print_user_info2(name, age, gender)

# 实参也可以直接写字面值
print_user_info2("悟空", 100, "male")
name: zhangsan
age: 10
gender: male
name: 悟空
age: 100
gender: female
name: 悟空
age: 100
gender: male

函数的参数:形参与实参

为了让函数处理不同的数据,我们需要给它传递参数。

  • 形参 (Formal Parameter):定义函数时设置的参数,是形式上的变量名。
  • 实参 (Actual Parameter):调用函数时传入的参数,是实际的值。

调用时,实参按顺序依次传递给形参;实参不仅可以是变量,也可以是直接写在调用处的字面值。

让函数调用更灵活:缺省参数与命名参数

缺省参数

在定义函数时,可以为某些参数指定一个默认值,这样的参数称为缺省参数。调用时,如果未提供该参数的值,则使用默认值。

定义规则:缺省参数必须放在参数列表的最后。

命名参数

调用函数时,可以通过指定参数名来传递值,这称为命名参数。使用命名参数时,可以不按形参定义的顺序传参;参数名必须与定义时的形参名称相同。

def x_y_sum(x, y=20):  # y 是缺省参数,默认值为 20
    return x + y

print(x_y_sum(10))      # 只传 x,y 使用缺省值 20
print(x_y_sum(10, 30))  # 传入 30 替换缺省值

def x_y_sum2(x=10, y=20):
    return x + y

print(x_y_sum2(y=30, x=15))  # 按参数名传,顺序可以与定义时不同
print(x_y_sum2(x=20))        # 只传 x,y 使用默认值
print(x_y_sum2())            # 都使用默认值
print(x_y_sum2(30))          # 只传第一个位置参数,y 仍用默认值
30
40
45
40
30
50

错误示例:将缺省参数放在非缺省参数前面会导致语法错误。

def wrong_sum(y=20, x):  # 错误!缺省参数 y 不能放在非缺省参数 x 前面
    return x + y

原因:如果只传一个参数 wrong_sum(10),Python 无法确定这个 10 是给 y 还是给 x。

函数中的变量:局部与全局

局部变量

在函数内部定义的变量称为局部变量。

  • 作用范围:仅在定义它的函数内部有效。
  • 独立性:不同函数内的局部变量即使同名,也互不影响。
  • 外部不可见:在函数外部或其他函数内部无法直接访问。
def func_a():
    x = 1  # func_a 的局部变量 x
    print("func_a 中的 x: {}".format(x))

def func_b():
    x = 2  # func_b 的局部变量 x,与 func_a 的 x 无关
    print("func_b 中的 x: {}".format(x))

func_a()
func_b()

# 常见误区:跨函数访问局部变量
def set_name():
    my_name = "张三"  # 局部变量

def get_name():
    print(my_name)  # 错误!此处无法访问 set_name 中的 my_name

set_name()
# get_name()  # 取消注释会报 NameError: name 'my_name' is not defined

# 正确做法:通过 return 将局部变量的值传递出来
def set_name2():
    name = "张三"
    return name  # 返回局部变量的值

def print_name(n):
    print(n)

retrieved_name = set_name2()  # 用外部变量接收返回值
print_name(retrieved_name)    # 将返回值作为参数传入
func_a 中的 x: 1
func_b 中的 x: 2
张三

全局变量

在函数外部定义的变量称为全局变量。

  • 作用范围:从定义开始,到整个文件结束,在函数内部也可以访问(读取)。
  • 修改限制:在函数内部修改全局变量的值,需要使用 global 关键字声明。
name = "张三"  # 全局变量

def get_name():
    print(name)  # 在函数内部可以读取全局变量

get_name()
print(name)  # 在函数外部也可以读取
张三
张三

修改全局变量:在函数内部修改全局变量的值,必须先用 global 关键字声明;具体现象和写法放到后面的练习讲解里完整展开。

命名建议:为了清晰区分,全局变量命名常以 g_ 开头(如 g_name),但这只是约定,并非语法要求;决定一个变量是不是全局变量的是它声明的位置,而不是名字。

📝 动手练一练

  1. 编写一个函数 greet(name, greeting="Hello"),它接受一个名字和一个可选的问候语(默认为 “Hello”),并打印问候语和名字。尝试用不同的方式调用它:只传名字、同时传名字和自定义问候语、使用命名参数颠倒顺序传入。
👉 点击查看参考答案

思路:定义一个带有缺省参数 greeting 的函数。调用时演示使用默认值、覆盖默认值以及使用命名参数。

def greet(name, greeting="Hello"):
    print("{}, {}!".format(greeting, name))

# 只传名字,使用默认问候语
greet("Alice")
# 同时传名字和自定义问候语
greet("Bob", "Good morning")
# 使用命名参数,颠倒顺序
greet(greeting="Hi", name="Charlie")
Hello, Alice!
Good morning, Bob!
Hi, Charlie!

常见错误

错误写法现象或报错原因正确写法
def func(a=1, b):SyntaxError: parameter without a default follows parameter with a default缺省参数 a=1 放在了非缺省参数 b 的前面。def func(b, a=1):
在函数内直接 全局变量 = 新值函数外该全局变量的值未被修改。未使用 global 声明,这行代码实际创建了一个同名的局部变量。在函数内使用 global 全局变量名 声明后再赋值。
在函数 A 中定义变量,在函数 B 中直接使用NameError: name 'xxx' is not defined局部变量的作用域仅限于其所在的函数内部。通过函数 A 的返回值将值传递给函数 B 的参数。
调用函数时,实参顺序与形参定义不匹配且未使用命名参数程序可能不报错,但逻辑错误,得到意外结果。实参会按位置顺序传递给形参。仔细核对顺序,或使用命名参数明确指定。

小结

  1. 函数用 def 定义,通过函数名加括号调用。
  2. 定义时的参数叫形参,调用时传入的值叫实参。
  3. 缺省参数让调用更简洁,但必须定义在参数列表末尾。
  4. 命名参数允许在调用时不按形参顺序传参。
  5. 局部变量在函数内定义,仅函数内有效;全局变量在函数外定义,整个文件有效。
  6. 在函数内修改全局变量,必须先用 global 关键字声明。

📋 行动清单

  • 将“动手练一练”中的代码自己敲一遍,并尝试修改参数,观察输出变化。
  • 创建一个全局变量 g_count = 0,写一个函数 increment(),在函数内用 global 声明并修改 g_count,调用几次后打印其值,验证修改是否生效。
  • 故意写一个错误的函数定义(如缺省参数在前),运行它,看看 Python 会报什么错。

—— 小象教研组

配套资料 · 直接下载

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

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