破损数据柜
Hoppy 走进试炼的第三个房间时,发现桌上已经摆着一份别人写好的小工具。它会读入数据柜记录,也会打印出一份总结,看起来好像一切正常。可档案官摇了摇头:最麻烦的脚本,往往不是直接报错的那种,而是“能跑、也有输出、但结果 quietly 不对”的那种。
所以这节课的重点不是再发明一套新流程,而是练真正会做事的人必须有的能力:读懂已有代码,发现变量名和实际逻辑哪里对不上,再把这份小工具修回可信的样子。
先练一种很重要的感觉:变量名说了什么,代码真的有做到吗?
修旧代码时,第一步通常不是盯着报错,而是顺着“数据长什么样”一路往下看。变量名、拆分位置、循环对象,这三样如果有一处对不上,结果就会开始悄悄歪掉。
row = "name=fern seal | room=north | count=2"
parts = row.split(" | ")
room_name = parts[0].split("=")[1]
print(room_name)
这里代码能运行,但 room_name 实际拿到的是 "fern seal",不是 "north"。所以读旧代码时,一个很好用的问题是:这个变量名和它真正取到的那一段,真的一致吗?今天的 starter 里,就有几处这种“小地方不对,后面整串都会偏”的问题。
今天的任务:修好一份已经能跑、但答案不可信的小工具
starter 已经帮你读好了 broken_cabinet.txt 和 cabinet_index.json。脚本也已经写出了大部分流程:清理文本、拆字段、补上 JSON 记录、去重、统计、做 summary。你的任务不是重写,而是把几处错误逻辑修回来,让这些步骤重新对齐。
这一步应该把前缀 "## " 去掉,把 "~" 变回空格,再把尾巴上的 "??" 清掉。只要这里少做一步,后面的名字和字段内容都会继续带着脏痕迹。
这份记录里有 drawer、cabinet、status 和 dust。修的时候重点不是背更多语法,而是确认每个变量到底对应哪一段,再用 cabinet_index[cabinet_code] 补上 room_name 和 keeper_name。
这份工具真正想保留的是“每个抽屉第一次出现的记录”,所以 seen_drawers 应该记住的是 drawer_name,而 room_counts 应该统计修好后的 unique_records,不是原始全部记录。
raw_row_count 应该真的是原始行数,unique_drawer_count 应该真的是唯一抽屉数,ready_unique_count 也应该建立在修正后的 status 上。修旧代码时,最后这一步特别重要:名字看起来对,不代表结果真的对。
这节课不是要你学习异常处理,也不是要你在一团谜题里乱试。starter 里只有几处明确的旧能力错误:清理动作不完整、字段读错、去重对象错了、统计范围偏了。你的任务是冷静地读懂它,然后把它修好。
参考答案点击展开点击收起
import json
with open("broken_cabinet.txt", "r", encoding="utf-8") as file:
cabinet_text = file.read().strip()
with open("cabinet_index.json", "r", encoding="utf-8") as file:
cabinet_index = json.load(file)
print("Broken cabinet text:")
print(cabinet_text)
print("Cabinet index:", cabinet_index)
cabinet_lines = cabinet_text.splitlines()
print("Cabinet lines:", cabinet_lines)
def clean_line(raw_line):
cleaned = raw_line.strip().replace("## ", "")
cleaned = cleaned.replace("~", " ")
cleaned = cleaned.replace("??", "")
return cleaned
def build_record(cleaned_line):
parts = cleaned_line.split(" | ")
drawer_name = parts[0].split("=")[1]
cabinet_code = parts[1].split("=")[1]
status = parts[2].split("=")[1]
dust_level = parts[3].split("=")[1]
cabinet_record = cabinet_index[cabinet_code]
return {
"drawer_name": drawer_name,
"cabinet_code": cabinet_code,
"status": status,
"dust_level": dust_level,
"room_name": cabinet_record["room_name"],
"keeper_name": cabinet_record["keeper_name"],
}
cleaned_lines = []
for raw_line in cabinet_lines:
cleaned_lines.append(clean_line(raw_line))
all_records = []
for cleaned_line in cleaned_lines:
all_records.append(build_record(cleaned_line))
seen_drawers = set()
unique_records = []
for record in all_records:
drawer_name = record["drawer_name"]
if drawer_name not in seen_drawers:
seen_drawers.add(drawer_name)
unique_records.append(record)
room_counts = {}
for record in unique_records:
room_name = record["room_name"]
if room_name not in room_counts:
room_counts[room_name] = 0
room_counts[room_name] += 1
ready_unique_count = 0
for record in unique_records:
if record["status"] == "ready":
ready_unique_count += 1
cabinet_summary = {
"raw_row_count": len(all_records),
"unique_drawer_count": len(unique_records),
"duplicate_row_count": len(all_records) - len(unique_records),
"ready_unique_count": ready_unique_count,
"room_counts": room_counts,
}
print("Cleaned lines:", cleaned_lines)
print("All records:", all_records)
print("Seen drawers:", seen_drawers)
print("Unique records:", unique_records)
print("Room counts:", room_counts)
print("Cabinet summary:", cabinet_summary)高级技巧想更进一步?点击展开点击收起
这节课最值得带走的,不只是“我修对了几个 bug”,而是你开始会检查一条数据流程有没有前后对齐:清理后的文本是什么,拆出来的字段是不是那一段,去重和统计到底在对谁工作。
下一课会把这种整合感再推到终点:不只是修一段现成脚本,而是完成整章世界观里的最后一次试炼。到那时,你会更明显地发现,真正学会的人,已经不只会跟着写,还会判断、修正、收束整个流程。