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

第 07 课:把函数组装成可反复使用的工具

学习目标:使用 init/add/summary 三个子命令,追踪参数到文件的路径,解释重启后数据为何仍在。
前置:前六课;会区分函数、对象、JSON 和文件。预计 65 分钟。
本课交付:一个由你自己创建的记录文件和跨两次运行的统计结果。

本课主线:使用工具时不再修改源代码

已有计算和校验函数,现在用命令行把输入交给它们。教材的 tracker.py 是入口,study_core.py 是可导入的功能模块。

先运行 py -3 tracker.py --help,看三个动作:init 创建文件、add 追加记录、summary 查看统计。帮助文字来自标准库 argparse。

先完成一次真实使用

在 starter-lab 目录依次运行:

py -3 tracker.py init
py -3 tracker.py add --subject Python --minutes 25
py -3 tracker.py add --subject 阅读 --minutes 15
py -3 tracker.py summary

最后预期:

记录数:2
总分钟:40
Python:25
阅读:15

打开 my-records.json,应有两条记录。关闭终端,重新进入同一目录,单独运行 summary,仍应得到 2/40。这次进程重新加载文件,没有沿用上一进程的内存。

若文件已存在,init 会拒绝覆盖。不要因此删除已有练习记录,换一个文件继续:

py -3 tracker.py --file another.json init
py -3 tracker.py --file another.json add --subject Python --minutes 25
py -3 tracker.py --file another.json summary

全局选项 --file 放在子命令之前。所选文件的父目录要已存在。

追踪一条 add 命令

终端参数(文本)
  → argparse:minutes 转 int,提取 subject
  → make_record:检查字段并整理主题
  → load_records:读取并校验已有 JSON
  → records.append:只修改本次内存列表
  → save_records:校验、转 JSON、写回文件
  → print:报告本次记录成功

add.add_argument("--minutes", type=int, required=True) 负责参数转换。输入 abc 在这个阶段就会失败;输入 -5 能转成整数,但会在 make_record 的业务范围检查失败。两层校验解决不同问题。

留意顺序:先确认新记录有效,再读取/保存;旧文件损坏时也不会直接按空列表覆盖。错误发生后不会打印“已记录”。

init 为什么用 x 模式

with args.file.open("x", encoding="utf-8") as file:
    file.write("[]\n")

x 表示独占创建,文件存在就报 FileExistsError;w 则可能清空已有内容。初始化的需求是创建新文件,因此选择 x。

with 保证离开这段代码时关闭文件。summary 的读取不改文件,add 才会调用写入函数。代码中哪一步会修改磁盘,应能明确指出。

汇总怎样按主题分组

第 04 课只算总数,完整版本还建立字典 by_subject:

by_subject[subject] = by_subject.get(subject, 0) + minutes

第一次看到 Python,get 返回默认 0,加上 25,存为 25;第二次看到 Python,取已有 25,再加 20,存为 45。键表示分组,值表示该组累计。

显示时使用 sorted,让主题按稳定顺序输出。统计的规则和显示顺序是不同职责,不需要把 print 塞进 summarize。

常见错误与边界

当前版本是单人教学工具。两个进程同时读旧文件再写回,可能互相覆盖;直接覆盖写入也不是断电恢复方案。需要这些保证时,下一阶段学习事务、原子替换或数据库,不能仅凭“用了文件”就宣称可靠。

动手练习与验收

用 --file my-test.json 新建另一份记录,录入 Python 25、阅读 15、Python 20。预期 3 条、60 分钟、Python 45、阅读 15。

然后尝试 -1 分钟:命令应失败,文件仍保持刚才三条。再次 init 应失败且保留旧数据。最后重启终端,查询结果仍相同。

看答案

区分验证失败前后的文件内容,比仅看到错误提示更能证明旧数据未被误改。

下一步

每次手动做以上检查很费力。下一课把关键行为写成可重复的测试,再独立实现一个筛选功能。