值、容器与控制流程
值、容器与控制流程 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
这一课用一批真实形态的学习记录练手:会读 list/dict 组成的数据,会用条件、循环和推导式做清洗统计,并且能预判 None 和可变性的坑。
问题场景
假设你从本站导出一批学习记录,形如:
records = [
{"course": "P07", "minutes": 25, "done": True},
{"course": "P08", "minutes": None, "done": False}, # 没学完,时长为空
{"course": "p07", "minutes": 25, "done": True}, # 编码不规范,大小写混了
]
需求很简单:总学习时长多少、每门课学了几次、没写时长的记录怎么算。真上手就会发现全是细节:None 怎么判、字典缺键怎么办、同名课程算几次。
核心解释
先钉三个值层面的行为,后面所有清洗都绕不开:
- 空值判断用
is None。minutes is None判断「没记录」,if not minutes会把 0 也误伤——学了 0 分钟和没记录是两回事。JS 的== null宽松比较在这里没有对应物,Python 判空要明确。 - 容器分可变与不可变。list 和 dict 可变(原对象被改,所有引用都看得到);str、tuple、int 不可变(只能换新对象)。
sorted(records)返回新列表,records.sort()原地修改,名字相近行为完全不同。 - 函数默认参数在定义时求值一次。
def f(items=[])的空列表是全局共享的,多次调用会越积越多;默认值该写None再在函数体内新建。
循环与统计的惯用写法:
def total_minutes(records: list) -> int:
total = 0
for r in records:
minutes = r.get("minutes") # 缺键返回 None,不抛 KeyError
if isinstance(minutes, int):
total += minutes
return total
def count_by_course(records: list) -> dict:
counts: dict = {}
for r in records:
key = r["course"].upper() # 先归一化再统计
counts[key] = counts.get(key, 0) + 1
return counts
unfinished = [r["course"] for r in records if not r["done"]] # 推导式:筛选+投影
dict.get(key, default) 是处理缺字段的正解;counts.get(key, 0) + 1 是计数累加的经典模式,比先判断再赋值少一个分支。
示例拆解
把上面拼成完整程序验证:
# main.py
records = [
{"course": "P07", "minutes": 25, "done": True},
{"course": "P08", "minutes": None, "done": False},
{"course": "p07", "minutes": 25, "done": True},
]
print(total_minutes(records)) # 50,None 被跳过
print(count_by_course(records)) # {'P07': 2, 'P08': 1},大小写归一
print(unfinished) # ['P08']
预期结果写在注释里,跑一遍对照。关键是能解释每个数字怎么来的:total_minutes 里 isinstance(minutes, int) 把 None 和字符串都挡在外面;count_by_course 先 upper() 归一化,所以 P07 计两次。这类统计逻辑将来会以同样的形状出现在 P19 的统计接口里,现在练的就是那课的地基。
动手任务
本课实验区有可直接运行的版本(标准库,本机运行输出结果):
- 加一个函数
max_course(records),返回学习时长最多的课程编号,全空时返回 None。 - 处理「时长为字符串」的脏数据:
"25"这类能安全转 int 的就转,转不动跳过。 - 用推导式写
clean(records),剔除缺少 course 键的记录,返回新列表。
常见错误
- 用
if value:判断有效值:0、空串、空列表全被当假,统计悄悄少数据;先分清「为空」和「为假」。 - 循环里边遍历边删列表元素:跳元素的经典事故;要么遍历副本,要么用推导式重建。
- 可变默认参数:
def f(items=[]),多次调用共享同一个列表,换成默认 None。 - copy 想当然:
rows[:]只浅拷贝,嵌套 dict 还是指向原对象,改内层照样互相影响。
检查与复盘
自查清单:
- 看到 dict 数据先想缺键、空值、类型不对三种情况再写逻辑
- 能说清 sorted 与 sort、is 与 == 的区别
- 计数、累加、筛选三个模式能脱手写出
参考资料
- Python 官方教程:数据结构 —— list/dict 方法与推导式
- Python 官方教程:控制流 —— 条件、循环与函数默认值
读完这一课了?
标记完成后,工作台会安排接下来的学习。