← 编程起步:做一个学习记录小助手

第 04 课:让汇总规则只写一次

学习目标:给函数传入记录,接收返回的字典,区分 return 与 print,画出调用链。
前置:第 03 课的字典列表和循环。预计 50 分钟。
本课交付:正常数据与空数据两次函数调用,以及一张输入到输出的图。

本课主线:同一个汇总规则服务不同输入

上一课的统计写在脚本顶层。现在想同时统计“今天”和“昨天”,不能每次复制一整段循环再换变量名。把“怎样汇总”写成函数,把“汇总哪一批记录”当参数传入。

先看函数的完整边界

versions/04_functions.py 中的核心代码:

def summarize(records):
    total = 0
    for record in records:
        total = total + record["minutes"]
    return {"count": len(records), "total_minutes": total}

def 定义函数。records 是参数名,它在这次调用中指向传进来的值。定义函数时不会立刻执行循环;写出 summarize(...) 才是调用。

运行 py -3 versions/04_functions.py,预期:

记录数:3
总分钟:60
空记录: {'count': 0, 'total_minutes': 0}

先核对内容与数字。最后一行字典是 Python 的显示格式,通常带单引号,尚不是 JSON 文件。

输入、处理、输出:三段职责

main 准备 records
    → summarize(records) 计算
    → 返回 {count: 3, total_minutes: 60}
    → main 读取两个字段并 print

函数返回值赋给 result 后,可以用于显示,也可以参与下一步判断:result["total_minutes"] >= 60。

如果只在 summarize 里 print(total) 而不 return,调用者会收到 None,就无法正常按键取值。这就是“让人看见”和“把结果交给另一段代码”的区别。

为什么输入要作为参数

假如函数不接收 records,而偷偷读取外面某个全局变量,同一条调用是否算今天还是昨天,就取决于那个变量最近被谁修改。

显式参数能让调用本身说明计算对象。函数每次用局部的 total = 0 开始,也不会把上一次调用的累计值带进来。

试着连续调用两次相同的列表,再调用空列表。预期前两次都是 60,第三次是 0。若第二次变成 120,检查是否误把 total 放到函数外并在多次调用之间复用了它。

入口为什么单独放到 main

文件末尾:

if __name__ == "__main__":
    main()

直接运行这个文件时,Python 将 __name__ 设成 "__main__",于是调用 main。以后其他文件导入本文件时,这个条件不成立,可以取得 summarize 函数而不自动运行演示。

这里先理解“定义能力”和“启动程序”两种职责即可,不需要背下所有导入机制。07 课把函数放到独立模块,会再次用到它。

常见错误与排错

动手练习与验收

增加一个独立函数 remaining_minutes(total, goal),返回还差的分钟数,最低为 0。

调用预期返回
remaining_minutes(25, 60)35
remaining_minutes(60, 60)0
remaining_minutes(75, 60)0

要求调用者自己决定怎样 print,函数里不打印。尝试用第二课 if/else 完成,不必急着寻找最短写法。

看答案

先算 goal - total,若结果小于 0 就返回 0,否则返回差值。

验收:给出三次返回值;解释 return 所在的缩进;手画 main → summarize → result → print,并指出循环在哪个节点发生。

下一步

汇总规则已独立,但记录仍写在 Python 文件里。下一课把数据移到 JSON,让使用者改记录时不必改程序。