学习目标:区分内存对象、JSON 文本和磁盘文件,能解释路径解析与 UTF-8 编码。
前置:第 04 课的参数和返回值。预计 55 分钟。
本课交付:改一份数据副本,在不改统计规则的情况下改变输出。
前几课每次启动都重新创建代码里那三条记录。程序中的列表存在内存里,程序结束后,这份运行中的对象就消失了。
文件用来保存能跨次运行读取的内容。JSON 是一种文本格式,它规定列表、对象、字符串、数字如何写在文本里;文件本身和 JSON 是两个概念。
磁盘上的 sample.json
→ read_text 读取 UTF-8 文本
→ json.loads 把文本解析成 Python 列表/字典
→ summarize 接收对象并统计
教材 data/sample.json:
[
{"subject": "Python", "minutes": 25},
{"subject": "阅读", "minutes": 15},
{"subject": "Python", "minutes": 20}
]
JSON 的键和字符串使用双引号;不能写 Python 注释;最后一项后面不能保留多余逗号。不要把 Python 字典打印出的单引号格式直接当作 JSON 文件。
运行:
py -3 versions/05_files.py
输出仍然是 记录数:3、总分钟:60。结果不变,改变的是数据来源。
关键代码:
path = Path(__file__).resolve().parent.parent / "data" / "sample.json"
text = path.read_text(encoding="utf-8")
records = json.loads(text)
result = summarize(records)
Path 是标准库 pathlib 提供的路径对象。__file__ 指当前脚本路径;第一个 parent 到 versions/,第二个到 starter-lab/;/ "data" / "sample.json" 拼接目标路径,不是在做数字除法。
这样即使从另一个终端目录运行脚本,也能找到随教材提供的样例。反过来,Path("my-records.json") 则相对当前工作目录,不是相对脚本;07 课将刻意用它保存用户选择的记录文件。
文件保存的是字节,读成文本需要指定编码。统一使用 UTF-8 能减少不同电脑默认编码不同导致的中文乱码。编辑器保存 JSON 时也选择 UTF-8。
json.loads(text) 的名字里 s 表示 string,它接收文本;json.dumps(records) 反过来把对象序列化成文本。dumps 不会自动保存文件,还需 write_text。
在一份练习脚本中试:
import json
records = [{"subject": "阅读", "minutes": 15}]
text = json.dumps(records, ensure_ascii=False, indent=2)
print(text)
print(json.loads(text) == records)
最后应为 True。ensure_ascii=False 让中文保持可读形式,indent=2 只影响缩进外观,不改变数据意义。
复制 sample.json 为 data/my-sample.json,在副本增加 {"subject": "英语", "minutes": 10}。复制 05_files.py 为自己的练习文件,只将读取的文件名改为 my-sample.json。
预期 4 条、70 分钟。统计函数不需要改,因为传入的数据形状没有变。
| 错误 | 所在阶段 | 排查方法 |
|---|---|---|
| FileNotFoundError | 找文件 | 打印 path,检查文件存在和扩展名 |
| UnicodeDecodeError | 字节转文本 | 确认文件保存编码为 UTF-8 |
| JSONDecodeError | 文本解析 | 按行列位置检查引号、逗号、括号 |
| KeyError / TypeError | 统计对象 | JSON 合法不代表字段正确,下一课补校验 |
每次只改一种错误,修复后再继续。不要捕获所有错误然后返回空列表,那会把“文件坏了”伪装成“没有学习记录”。
准备三份数据:原始样例、增加 10 分钟的副本、空列表 []。预期分别为 3/60、4/70、0/0。
再复制一个坏样例,故意缺一个逗号,运行并指出错误发生在读取路径、解码、解析、统计中的哪一步,然后修复。
缺逗号的 JSON 文本通常能读入字符串,但 json.loads 在语法解析阶段抛出 JSONDecodeError;尚未进入汇总。
把分钟数改成 -5,JSON 仍然合法,统计却失去意义。下一课明确数据契约,并给错误输入一个可解释的出口。