多个函数协作:
完整成绩报告
第十四天,我不再让一个函数承担全部工作,而是把总分、平均分、等级和最高分拆给不同函数,再用一个总管函数组织它们,完成一份完整的成绩分析报告。
让平均分函数调用总分函数
第一个练习仍然从熟悉的总分和平均分开始,但这次 calculate_average() 不再自己循环累计,而是调用 calculate_total() 取得总分。
def calculate_total(scores): # 接收成绩列表
total = 0 # 总分从0开始累计
for score in scores: # 逐个取出成绩
total = total + score # 加入已经累计的总分
return total # 返回最终总分
def calculate_average(scores): # 接收同一份成绩列表
if len(scores) == 0: # 先保护空列表
return 0 # 避免后面除以0
total = calculate_total(scores) # 调用另一个函数取得总分
average = total / len(scores) # 计算平均分
return average # 返回浮点数平均分
赋值语句 total = calculate_total(scores) 要先看右侧:程序进入总分函数,得到返回值后,左侧的 total 才接住它。程序整体仍然从上到下执行,只是在遇到函数调用时暂时进入另一个函数。
直接调用与间接调用
当主程序调用 calculate_average() 时,它的内部还会继续调用 calculate_total()。这属于间接调用。今天的小测最初只数到了两处直接调用,随后沿着函数内部的路线重新追踪,确认完整程序中总分函数实际执行了四次。
看到一条函数调用时,不能只数写在主程序里的函数名,还要继续检查被调用的函数内部是否调用了其他函数。
把平均分交给等级函数
平均分计算完成后,返回值继续传入 get_level()。测试列表 [95, 85, 90] 的总分是 270,平均分是浮点数 90.0,因此第一个条件 average >= 90 成立,等级为“优秀”。
def get_level(average): # 接收已经算好的平均分
if average >= 90: # 测试优秀的边界
return "优秀" # 90.0会进入这里
elif average >= 80: # 前面不满足时再判断良好
return "良好" # 返回等级并结束函数
elif average >= 60: # 继续判断是否及格
return "及格" # 返回及格
else: # 低于60分
return "不及格" # 返回不及格
/ 并不是固定保留一位小数,而是会返回浮点数;这次刚好显示为 90.0。这个数据随后由形参 average 接收并参与条件判断。
用总管函数组织完整报告
create_report() 不亲自遍历成绩,而是负责安排其他函数工作。每个小函数只做一件事,返回的结果分别由变量接住,最后汇总成字典。
def create_report(scores): # 定义组织其他函数的总管函数
total = calculate_total(scores) # 接住总分函数的返回值
average = calculate_average(scores) # 接住平均分函数的返回值
level = get_level(average) # 把平均分继续传给等级函数
highest = find_highest_score(scores) # 接住最高分函数的返回值
report = {"total": total, "average": average,
"level": level, "highest": highest} # 汇总字典
return report # 返回完整报告
final_report = create_report(student_scores) # 接住返回的字典
print("完整报告:", final_report) # 输出四项结果
print("最高分是:", final_report["highest"]) # 按键读取最高分
最终运行结果为 {'total': 270, 'average': 90.0, 'level': '优秀', 'highest': 95}。其中 final_report 是字典,"highest" 是键,读取到的 95 是值。
独立加入最高分功能
最后的独立练习是把昨天掌握的最高分比较重新写进今天的程序。第一次已经正确完成空列表保护、逐项比较和循环外返回,但最高分还在总管函数之外单独输出。订正后,将函数移到调用发生之前,并由 create_report() 调用,最高分才真正成为完整报告的一部分。
整理程序时还发现一条没有继续使用的独立最高分调用。删除它不会改变结果,却能避免重复计算。这让我开始注意:程序写完后,还要检查重复调用和没有使用的变量。
今天的小测与订正
calculate_average()会间接调用calculate_total()- 完整程序中的直接调用和间接调用都要计入执行次数
- 字典表达式左侧带引号的
"level"是键,右侧不带引号的level是保存结果的变量 - 执行函数调用前,被使用的函数定义必须已经执行完成
90.0是浮点数,并且满足>= 90的优秀边界
今天真正弄懂了什么
- 一个函数可以调用另一个函数,并接住它的返回值
- 函数调用可能继续进入下一层函数,需要沿调用路线追踪
- 可以把较长任务拆给多个职责清楚的小函数
- 总管函数可以组织多个函数并汇总它们的结果
- 字典可以保存总分、平均分、等级和最高分四项信息
- 测试边界时既要看数值,也要注意浮点数类型
- 整理代码时应删除重复调用和没有使用的变量
今天的程序第一次超过五十行,但每个函数仍然只负责一小块任务。代码变长并不等于每一部分都变难,关键是沿着调用关系,逐步看清数据从哪里来、被谁接住、最后返回到哪里。