函数
学生信息管理系统的功能不断增加时,如果所有代码都写在一个 while 循环中,会出现两个问题:
- 主循环越来越长,不容易阅读
- 相同逻辑可能重复出现,不方便修改
如何把菜单显示、学生查询、数据保存等功能拆开,并在需要时重复使用?函数可以把一段职责明确的代码单独封装起来:接收数据,完成任务,再把结果交回来。
函数作用与分类
先用四个问题明确函数的作用:
- 如何把大程序拆成多个小功能?把每段职责明确的代码封装成函数。
- 如何减少重复代码?需要时重复调用同一个函数。
- 如何让代码结构更清楚?让主流程只负责组织调用。
- 如何方便修改和复用?集中修改函数内部逻辑即可。
例如,学生管理系统可以拆成“显示菜单”“新增学生”“查询学生”“保存数据”等函数。主循环只负责根据用户选择调用对应功能。
函数分类: 按照来源,函数可以简单分成四类:
| 类型 | 示例 | 使用方式 |
|---|---|---|
| 内置函数 | len()、print() | 可以直接调用 |
| 标准库函数 | random.randint() | 导入标准库模块后调用 |
| 第三方库函数 | 第三方包提供的函数 | 安装并导入第三方包后调用 |
| 自定义函数 | 自己使用 def 编写的函数 | 先定义,再调用 |
这一章重点学习自定义函数。
函数定义与调用
使用 def 定义函数。def 是 define 的缩写,表示“定义”。
基本结构如下:
def 函数名(参数):
函数体
return 返回值一个函数通常包含这些部分:
def:定义函数的关键字- 函数名:调用函数时使用的名字
- 参数:函数接收的数据,可以没有
- 函数体:函数要执行的代码,必须缩进
return:把结果返回给调用者,可以没有
函数名要遵守标识符规则,通常使用小写字母和下划线。名字尽量表达功能,例如 show_menu、calculate_total。
定义:
先定义一个没有参数的函数,观察定义阶段发生了什么:
def show_menu():
print("1. 新增学生")
print("2. 显示学生")
print("0. 退出系统")运行这段代码时不会显示菜单,因为这里只是定义函数,还没有调用。
调用:
在函数名后加小括号,就是调用函数:
def show_menu():
print("1. 新增学生")
print("2. 显示学生")
print("0. 退出系统")
show_menu()运行结果:
1. 新增学生
2. 显示学生
0. 退出系统代码执行到 show_menu() 时,会进入函数执行函数体。函数体结束后,再回到调用位置继续向下执行。
定义顺序与调用
函数调用执行时,函数名必须已经定义:
show_menu() # NameError
def show_menu():
print("学生信息管理系统")Python 从上到下执行代码。运行到第一行时还不知道 show_menu 是什么,因此会出现 NameError。
定义不等于执行
执行 def 只会创建函数,不会运行函数体。只有调用函数时,函数体才会执行。
参数与返回值
没有参数的函数只能完成固定任务。加入参数后,同一个函数就能处理不同数据。
参数接收
例如,函数 f(x) 表示公式:
y = 5x + 6可以这样封装:
def f(x):
y = 5 * x + 6
return y
print(f(5))
print(f(6))
print(f(7))运行结果:
31
36
41函数逻辑没有变化,只是每次传入的 x 不同,所以得到了不同结果。
如果公式中的 k 和 b 也需要变化,可以继续增加参数:
def calculate_y(x, k, b):
return k * x + b
print(calculate_y(5, 5, 6))
print(calculate_y(5, 2, 3))参数的类型、顺序和更多写法,会在下一节集中讲解。
返回值与执行流程
函数完成计算后,可以用 return 把结果交给调用者:
def calculate_total(chinese, math, english):
total = chinese + math + english
return total
student_total = calculate_total(88, 92, 85)
print(student_total)运行结果:
265返回值可以保存到变量中,也可以继续参与计算或传给其他函数。
输出与返回: print() 只是把内容显示在控制台,return 才会把结果交给函数外部:
def print_total(chinese, math):
print(chinese + math)
result = print_total(88, 92)
print(result)运行结果:
180
None函数没有执行 return 时,默认返回 None。
提前结束: return 有两个作用:
- 返回结果
- 立即结束当前函数
def get_result():
return "函数结束"
print("这行代码不会执行")
print(get_result())程序执行到 return 后会立刻离开函数,所以后面的 print() 永远不会执行。
多分支返回: 函数里可以编写多个 return,但一次调用只会执行其中一条路径。
下面定义一个函数,判断数字是否为素数:
什么是素数
素数也叫质数,指大于 1,并且只能被 1 和它本身整除的整数。
def is_prime(number):
if number <= 1:
return False
for divisor in range(2, number):
if number % divisor == 0:
return False
return True
for number in range(100):
if is_prime(number):
print(number, end=" ")运行结果:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97只要找到一个能整除 number 的数,函数就会立即返回 False。循环全部结束后还没有找到,才返回 True。
容器与多值返回
如果一次计算要交回一组数据,函数可以返回列表、字典等容器;如果要交回多个结果,也可以一次返回多个值。
返回列表:
下面的函数生成一个包含随机数字的列表:
import random
def create_random_list(count):
numbers = []
for _ in range(count):
numbers.append(random.randint(0, 9))
return numbers
result = create_random_list(5)
print(result)每次运行得到的数字可能不同,但列表长度都是 5。
返回多个值:
需要同时返回多个结果时,可以在 return 后写多个值:
def get_score_info(scores):
highest = max(scores)
lowest = min(scores)
total = 0
for score in scores:
total += score
return highest, lowest, total
result = get_score_info([88, 92, 85])
print(result)
highest, lowest, total = result
print(highest, lowest, total)运行结果:
(92, 85, 265)
92 85 265看上去返回了多个值,实际返回的是一个元组。上一节学习的序列解包,可以直接把元组中的结果交给多个变量。
函数形式
根据“有没有参数”和“有没有返回值”,函数可以分成四种形式。遇到一个新任务时,可以先判断它属于哪一种:
| 形式 | 示例用途 |
|---|---|
| 无参数、无返回值 | 显示固定菜单 |
| 无参数、有返回值 | 获取当前时间 |
| 有参数、无返回值 | 打印指定学生信息 |
| 有参数、有返回值 | 根据三科成绩计算总分 |
最常用的是“有参数、有返回值”:参数让函数能够处理不同数据,返回值让计算结果可以继续使用。
函数练习
下面用五个小任务练习函数的定义、参数和返回值。
百以内累加:
def get_sum():
total = 0
for number in range(1, 101):
total += number
return total
print(get_sum())运行结果:
5050range(1, 101) 会生成 1 到 100,不包含结束位置 101。
两数最大值:
参考答案
def get_max(number1, number2):
if number1 > number2:
return number1
return number2
print(get_max(1, 3))
print(get_max(11, 3))范围累加:
参考答案
def sum_to_n(n):
total = 0
for number in range(1, n + 1):
total += number
return total
print(sum_to_n(10))两数平均值:
参考答案
def calculate_average(number1, number2):
return (number1 + number2) / 2
print(calculate_average(80, 90))调和数计算:
第 n 阶调和数的计算方式如下:
1 + 1/2 + 1/3 + ... + 1/n参考答案
def harmonic(n):
total = 0
for number in range(1, n + 1):
total += 1 / number
return total
print(harmonic(5))案例:计算复利
需求:对多组本金、利率和年限计算最终金额,并复用同一套计算逻辑。
功能分析:先把复利公式封装成函数,再用两层循环处理不同方案和年限。
复利公式如下:
最终金额 = 本金 × (1 + 年利率) ^ 年限完成下面的计算:
- 本金
10000元、年利率2.561% - 本金
10000元、年利率6% - 本金
20000元、年利率6% - 每组分别计算
5年、10年、15年和20年后的金额
先把公式封装成函数:
def calculate_balance(principal, interest, years):
balance = principal * (1 + interest) ** years
return balancePython 中使用 ** 计算乘方。利率需要转换成小数,例如 6% 写成 0.06。
接下来准备三组方案和四个年限,再重复调用函数:
def calculate_balance(principal, interest, years):
balance = principal * (1 + interest) ** years
return balance
plans = [
(10000, 0.02561),
(10000, 0.06),
(20000, 0.06),
]
year_options = [5, 10, 15, 20]
for principal, interest in plans:
for years in year_options:
balance = calculate_balance(principal, interest, years)
print(
f"本金:{principal} 元,"
f"年利率:{interest:.3%},"
f"年限:{years} 年,"
f"最终金额:{balance:.2f} 元"
)函数只负责计算复利。外层循环负责取出不同方案,内层循环负责处理不同年限。以后公式变化时,只需要修改函数内部。
小结
本节围绕四个问题掌握函数基础:
- 如何封装并调用一段功能?使用
def定义函数,通过函数名加小括号调用。 - 如何让同一个函数处理不同数据?通过参数接收数据。
- 如何把结果交给函数外部?使用
return,它还会立即结束当前函数。 - 如何减少重复代码?把程序拆成职责明确、可以复用的函数。
练习
- 编写函数,接收圆的半径并返回面积
- 编写函数,接收两个数并返回较小值
- 编写函数,接收
n并返回1到n之间所有偶数的和 - 把学生管理系统的菜单输出封装成
show_menu()函数