第25章-图书管理小程序AI协作开发


今天不学新语法,做项目。目标一个:图书管理小程序——添加图书、查看全部、按书名搜索、删除图书,数据存文件,关了程序再打开数据还在。

项目本身不大,上周你学过的函数、字典、JSON 都够用。今天的重点不在代码,在流程纪律:一个需求从"一句话"变成"能用的程序",中间每一步该干什么,都要有明确动作,别跳步、别偷懒。这一套流程走完,你就从"会抄代码"升级成"会和 AI 组队做项目"。

先看需求原文,就一句话:

图书管理小程序:添加图书(书名 + 作者 + 价格)、查看全部、按书名搜索、删除图书,数据存 books.json

第一步:把一句话需求翻译成四要素

这句话信息量不够,直接丢给 AI 会有一堆空白点:价格怎么显示?删除按什么删(序号还是书名)?搜索不到怎么办?删除不存在的书怎么办?所以第一步是把需求"翻译"成四要素,把空白点全部补上。

四要素 这段需求怎么说
背景/角色 我是零基础初学者,学过函数、字典、JSON 文件
目标 写一个图书管理小程序,能添加、查看、搜索、删除图书
输入输出 菜单选择功能;添加时输入书名/作者/价格;搜索输入书名;显示图书列表
约束 数据存 books.json;用函数封装;加中文注释;只用标准库;尽量简单

翻译的时候,有几个细节必须自己先拍板,这就是"翻译"的含金量:

  • 删除按什么删:最简单的方案是按序号删(显示列表时带序号),新手项目推荐这个。
  • 价格怎么显示:保留两位小数(后面第 4 步的追问就靠它)。
  • 图书数据长什么样:每本书是一个字典 {"书名": ..., "作者": ..., "价格": ...},多本书组成列表,存进 JSON。

需求里没写清"删除按什么删",AI 会自己选一个方案(可能是按书名删),你就要多一轮追问——所以翻译时主动补全。中文输入法打菜单序号注意别打成全角数字(123),Python 不认全角数字。提示词写完对照四要素自查一遍再发,别急着点发送——发出去一次写对的概率,取决于你现在多想了一分钟。

翻译好的完整提示词(这就是第 1 步的输入,直接复制):

【背景 / 角色】我是 Python 零基础初学者,学过变量、列表、字典、函数,学过 JSON 文件读写。
【目标】请帮我写一个图书管理小程序:能添加图书、查看全部、按书名搜索、删除图书。
【输入输出】
  输入:用户从键盘选择菜单功能,输入书名、作者、价格。
  输出:屏幕上显示图书列表、搜索结果或操作提示。
  交互流程:
  1. 显示菜单:1 添加图书,2 查看全部,3 按书名搜索,4 删除图书,5 退出
  2. 添加时依次输入书名、作者、价格
  3. 查看时打印全部图书,每本带序号
  4. 搜索时输入书名,打印匹配的图书
  5. 删除时输入图书序号,删除并提示"删除成功"
【约束】数据保存到 books.json 文件,程序重启后数据还在;用函数封装每个功能;
代码加中文注释;只用 Python 标准库;尽量简单,适合零基础阅读。

全局路线图:6 步走完一个项目

拿到 AI 生成的代码之后,今天一整天的开发都按下面的 6 步走,每一步都有明确的"产出物":

步骤 做什么 产出物 对应本周技能
第 1 步 用四要素让 AI 生成完整代码 一份初版代码 周三:四要素
第 2 步 复制到 Cursor 运行,挨个功能测试 一份测试记录(哪些通过、哪些有 bug) 周一~二:运行与调试
第 3 步 有 bug 用三步法提问修复 修好的代码 周四:三步法
第 4 步 提"小需求"追问修改 加了细节的代码 周四:追问修改
第 5 步 让 AI 加注释,逐行读,把不懂的问明白 一份自己看得懂的代码 周四:解释代码
第 6 步 不看 AI 答案,自己再写一遍 自己独立写的一版 全部(独立能力)

为什么是这个顺序?因为它模拟了真实开发:先有能跑的东西(1-2)→ 修到稳定(3)→ 迭代需求(4)→ 把它变成自己的(5-6)。顺序不要乱,一句话概括:先跑通、再改造、最后独立写

第 2 步和第 3 步容易混着做:一边测试一边顺手改代码。不行,测试要"记录问题",修 bug 要"走三步法",分开才是训练。每一步做完都留痕:把测试结果、报错、追问记录写进笔记,这是你积累的"开发日志"。第 5 步最容易被跳:代码能跑就懒得读了。但读不懂的代码 = 不属于你的代码,下周的项目还要用它,跳步早晚还债。

第 2 步:拿到代码先别改,逐个功能测一遍

拿到 AI 生成的代码,第一件事不是改,是。怎么测才叫"测到位"?每个功能都要测两种场景:

功能 正常情况测试 边界情况测试(最容易藏 bug)
添加图书 添加 3 本书,看列表是否出现 价格输负数 / 输文字会怎样
查看全部 打印所有书,格式是否整齐 一本都没加时打印什么
按书名搜索 搜存在的书,能找到 搜不存在的书,会不会崩溃
删除图书 按序号删,提示"删除成功" 删不存在的序号,会不会崩溃
数据保存 重启程序,数据还在 手动删空 books.json 再运行

测试的正确姿势:

  1. 每测一项,在纸上(或笔记里)记:功能名 + 结果(通过 / 报错)。
  2. 报错就把错误截图/复制下来,等会走三步法。
  3. 重启程序验证数据持久化(第 1~4 周学到 JSON 时强调过:程序关掉再开,数据还在才叫"存住了")。

"能跑"不等于"没问题":只测正常情况,边界情况(空数据、非法输入、越界序号)会漏掉一半 bug。测试时把终端窗口拉大,报错信息太长时用滚动条往上翻,从第一行 Traceback 开始复制。发现 bug 别顺手改代码,按第 3 步流程走——现在练的是流程,不是速度。

第 3、4 步:修 bug 走三步法,加细节用追问

第 3 步(修 bug):测试记录里只要有报错,就按周四的"三步法"提问:

1. 发生了什么:我在运行图书管理小程序,<哪个功能>时报错。
2. 报错原文:
<粘贴完整报错>
3. 我是 Python 零基础初学者,请用大白话解释原因,并给出修改后的完整代码。

第 4 步(追问修改):程序能跑、bug 修完后,提小需求让程序更精致。本周推荐两个素材:

代码能跑了,请帮我改两处:
1. 所有价格显示保留两位小数,比如 19.9 元显示成 19.90 元
2. 搜索时按书名模糊匹配(输入"三"能搜到"三国演义")
只修改这两处,其他代码不要动。

注意顺序:先修 bug(第 3 步),再改需求(第 4 步)。程序还是坏的就去改功能,越改越乱。

一个报错发一次提问,别攒三个报错一起发——AI 分不清谁是谁。追问前确认代码能跑;追问后重新跑一遍全部功能,防止 AI 改坏旧功能。每次 AI 改完都验证:"只改这些"是给 AI 的要求,验证是自己的责任,AI 也可能失手。

第 5 步:让 AI 加注释,把代码变成自己的

程序能跑、功能齐了,还差最后一步"内化":让 AI 给代码加注释,然后你逐行读、逐行懂。

动作分解:

  1. 让 AI 加注释:
请给这段代码的每个函数、每个关键行加中文注释,代码逻辑不要动,只加注释。
<粘贴完整代码>
  1. 逐行读一遍,对每一行问自己:这行在干什么?为什么要这么写?
  2. 看不懂的行,直接追问 AI:
第 X 行(<粘贴这一行>)为什么这么写?换个写法行不行?请用大白话解释。
  1. 把"原来不懂、现在懂了"的点抄进笔记——这是你成长的记录。

推荐配一张"读懂检查表",看完代码你能回答这些问题,才算真懂:

  • 程序的数据是怎么存的?为什么选择这种结构?
  • 每个函数分别负责什么?函数之间怎么传递数据?
  • 搜索和删除是怎么找到那本书的?(遍历列表 + 判断,这是核心逻辑)
  • 如果我要加一个"修改图书价格"功能,应该加在哪里?

加注释时一定写"代码逻辑不要动,只加注释"——否则 AI 可能顺手重写。读代码读不懂是正常现象,不是笨:每个人第一次读别人的代码都懵,多读多问就会了。"把看不懂的问明白"是第 5 步的灵魂:留着不懂的代码过夜,第 6 步你会写不出来。

第 6 步:关掉答案,自己重写

第 6 步是整周的压轴动作:关掉 AI 的答案,新建一个文件,自己把这个程序完整写一遍

为什么必须这么做?眼睛看懂了 ≠ 手会写。就像看教练游泳 10 遍,自己下水照样呛水。考试和面试的时候没有 AI,只有你脑子里的东西。自己重写的过程还会暴露"假懂"的地方:卡住的那一行,就是你还没掌握的那个知识点。

操作方法(口诀:写不出就对照,再写):

  1. 关掉 AI 的答案(最小化或另开一个文件),打开空文件开始写。
  2. 按功能拆解:先写菜单循环 → 再写添加 → 查看 → 搜索 → 删除 → JSON 保存。
  3. 卡住时:看一眼 AI 的答案里对应那一段,看完关掉,凭记忆继续写。
  4. 写完运行,全功能测试一遍,和 AI 版本对比,把差异抄进笔记。
  5. 如果时间允许,再写第二遍——第二遍应该能一气呵成。

别把第 6 步做成"对着抄":抄一遍没有训练效果,卡住再看、看完再关,才是有效练习。别因为"AI 写的比我好"就放弃——你现在写不好很正常,正因为写不好才要练。重写不要求一字不差:功能相同、逻辑清晰,你的写法完全可以比 AI 的更好。

参考实现:一份带注释的完整代码

下面这份代码是"第 5 步读的素材 + 第 6 步对照的标准答案"。先按第 5 步的流程逐行读懂它,再在最后一步合上它自己重写。代码用函数封装(呼应第 4 周),没有用类——本周类的重点是"看懂",项目用函数更简单直接。

import json          # 处理 JSON 文件
import os            # 判断文件是否存在

BOOK_FILE = "books.json"   # 数据文件名(常量用全大写命名)

# ---- 工具函数:读写 JSON 文件 ----

def load_books():
    """从文件读取图书列表;文件不存在或损坏时返回空列表"""
    if not os.path.exists(BOOK_FILE):     # 第一次运行,文件还不存在
        return []
    with open(BOOK_FILE, "r", encoding="utf-8") as f:
        return json.load(f)               # 把文件内容转成 Python 列表

def save_books(books):
    """把图书列表写回文件"""
    with open(BOOK_FILE, "w", encoding="utf-8") as f:
        json.dump(books, f, ensure_ascii=False, indent=2)   # 存中文不乱码

# ---- 四大功能 ----

def add_book(books):
    """添加一本书"""
    name = input("请输入书名:")
    author = input("请输入作者:")
    price = float(input("请输入价格:"))    # 字符串转小数
    books.append({"书名": name, "作者": author, "价格": price})
    save_books(books)                       # 立刻保存
    print("添加成功!")

def show_books(books):
    """查看全部图书,每本带序号"""
    if not books:                           # 空列表时
        print("暂无图书,请先添加。")
        return
    for i, book in enumerate(books, start=1):   # 从 1 开始编号
        print(f"{i}. 《{book['书名']}》 作者:{book['作者']} 价格:{book['价格']:.2f} 元")

def search_book(books):
    """按书名搜索"""
    keyword = input("请输入要搜索的书名:")
    found = [b for b in books if keyword in b["书名"]]   # 模糊匹配
    if not found:
        print("没有找到相关图书。")
    else:
        for b in found:
            print(f"《{b['书名']}》 作者:{b['作者']} 价格:{b['价格']:.2f} 元")

def delete_book(books):
    """按序号删除图书"""
    show_books(books)                       # 先显示列表,让用户看到序号
    try:
        num = int(input("请输入要删除的图书序号:"))
        if 1 <= num <= len(books):          # 序号必须在有效范围内
            removed = books.pop(num - 1)    # 列表下标从 0 开始,所以减 1
            save_books(books)
            print(f"已删除《{removed['书名']}》。")
        else:
            print("序号无效!")
    except ValueError:                      # 用户输入了非数字
        print("请输入数字!")

# ---- 主程序:菜单循环 ----

def main():
    books = load_books()                    # 启动时读入已有数据
    while True:
        print("\n===== 图书管理系统 =====")
        print("1. 添加图书")
        print("2. 查看全部")
        print("3. 按书名搜索")
        print("4. 删除图书")
        print("5. 退出")
        choice = input("请选择功能(1-5):")
        if choice == "1":
            add_book(books)
        elif choice == "2":
            show_books(books)
        elif choice == "3":
            search_book(books)
        elif choice == "4":
            delete_book(books)
        elif choice == "5":
            print("再见!")
            break
        else:
            print("请输入 1-5 之间的数字!")

if __name__ == "__main__":      # 只有直接运行本文件时才执行
    main()

运行起来是这个样子(交互过程):

===== 图书管理系统 =====
1. 添加图书
2. 查看全部
3. 按书名搜索
4. 删除图书
5. 退出
请选择功能(1-5):1
请输入书名:三国演义
请输入作者:罗贯中
请输入价格:19.9
添加成功!

===== 图书管理系统 =====
请选择功能(1-5):2
1. 《三国演义》 作者:罗贯中 价格:19.90 元

读这份代码时,三个地方容易看走眼:

  • json.dump(..., ensure_ascii=False, indent=2) 两个参数要记牢:不写 ensure_ascii=False 中文会变成 \u4e09 转义码,不写 indent 文件会挤成一行。
  • books.pop(num - 1) 是新手最常错的地方:序号从 1 开始、下标从 0 开始,中间差 1。
  • 空列表(if not books)、非法输入(try/except)、序号越界——这份参考代码把三个边界都处理了,这也是测试清单里"边界情况"对应的代码。

随堂练习

按 AI 协作 6 步流程开发,每步完成打勾:

  • [ ] 第 1 步:用上面的四要素提示词让 AI 生成完整代码
  • [ ] 第 2 步:复制到 Cursor 运行,按测试清单测试:添加 3 本书 → 查看 → 搜索 → 删除,全部测一遍,记录结果
  • [ ] 第 3 步:故意测试边界——搜索不存在的书、删除越界序号、价格输负数,有 bug 就走三步法
  • [ ] 第 4 步:用追问模板改"价格保留两位小数"等小需求,改完重新全功能测试
  • [ ] 第 5 步:让 AI 加注释,对照"读懂检查表"逐行读一遍,不懂的抄进笔记并追问 AI
  • [ ] 第 6 步:关掉 AI 答案,自己独立重写一遍;写完和参考实现对比,差异抄进笔记
  • [ ] 最后对着本周复习清单自查(见下方),全部打勾才算过关

本周复习清单(对着自查,都是考试范围):

  • [ ] 能说出类、对象、__init__self 分别是什么
  • [ ] 能看懂封装(_ 开头)和继承(class Cat(Animal))的例子
  • [ ] 写代码能用"四要素"说清需求
  • [ ] 报错能粘贴原文用"三步法"求助
  • [ ] 追问时记得加"只改这些,其他不要动"
  • [ ] 图书管理小程序完整跑通一遍(含数据持久化)

进阶题 ★:给小程序加一个"修改价格"功能

  1. 用追问模板让 AI 加:菜单加"6 修改价格",按序号找到书后重新输入价格。
  2. 追问完自己独立把这段逻辑实现一遍(不许看 AI 代码)。
  3. 测试:修改后列表和 books.json 文件里的价格都变了。

直接复制的提示词

场景一:让 AI 复盘你的项目流程(收尾检查)

我是零基础初学者,今天用 AI 协作完成了一个图书管理小程序。请按下面的问题帮我复盘,
每个问题用 2-3 句大白话回答:
1. 我的四要素提示词有没有遗漏信息?哪些是你本来会猜的?
2. 我的代码里哪些地方最容易出 bug?
3. 如果我想加"修改图书价格"功能,改哪些函数就行?

场景二:第 5 步逐行读懂代码(对照参考实现用)

请逐行解释下面这段代码在做什么,用大白话,适合零基础;重点讲清楚:
1. load_books 和 save_books 为什么一个返回一个不返回
2. 删除时 pop(num - 1) 为什么要减 1
3. try/except 是干什么的
<粘贴你的代码>

报错速查

报错信息 什么意思 怎么办
FileNotFoundError: [Errno 2] No such file or directory: 'books.json' 读文件时文件不存在 打开前判断:if not os.path.exists(...) 先返回空列表
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 books.json 是空文件或内容不是合法 JSON 删除空 books.json 让它重建,或打开前用 try/except 兜底
TypeError: string indices must be integers 把字符串当列表/字典用了,通常是遍历对象不对 检查 for 遍历的是不是列表(打印 type(数据) 验证)
IndexError: pop index out of range 删除序号越界,pop(9) 但列表只有 3 项 删之前判断 1 <= num <= len(books)
ValueError: invalid literal for int()/float() 输入价格时输了非数字 try/except ValueError 拦截,提示"请输入数字"
UnicodeEncodeError / JSON 里中文变成 \u 保存中文出问题 写文件加 encoding="utf-8"json.dumpensure_ascii=False

周末安排:复习 + 周测

今天把五天的东西串成了一条流水线:四要素把需求说清楚(周三),测试把问题找出来,三步法修 bug、追问加细节(周四),读懂代码、再自己写一遍,项目就成了。这条流水线也是你以后每个项目的标准套路,下周开始你会一个人用它做更大的项目。

明天周六是本周周测,今晚把上面的复习清单过一遍。下周一进入第 6 周:阶段项目一(通讯录 / 记账本),比今天这个项目大一号,考验的就是今天的这套流程——四要素、三步法、追问,下周全都要靠它们吃饭。

本周测评(周六周测说明)

周测形式:上机操作题(约 40 分钟,在电脑上实际敲代码完成,不是笔试背概念)

考核要点(4 道题,对应本周 4 个主题):

题号 考核内容 对应章节 分值建议
定义一个类并创建对象(如 Student 类 + show 方法 + 创建 2 个对象调用) 第 21 章 操作为主
写一个继承的例子(如 class Cat(Animal),体现父类方法被子类复用) 第 22 章 操作为主
用"四要素"向 AI 描述一个需求(文字题:给你一个需求,写出完整提示词) 第 23 章 文字题
给一段报错信息,写出"三步法"求助内容(文字题:按三步格式写出要发给 AI 的话) 第 24 章 文字题

评分标准:

  • 以操作为主、理论为辅,完成 60% 即通过。
  • 题目①、②重点看代码能否运行、对象能否正确调用方法;题目③、④重点看格式完整(四要素缺一项扣分、三步法缺原文粘贴扣分)。
  • 周五项目(图书管理小程序)能完整跑通可作为加分项。

未通过怎么办:

  • 周日到教室补测,只补做错题对应的那部分(比如只错了四要素那道题,补测就只考四要素)。
  • 补测前建议:重做第 23、24 章的随堂练习(四要素填空 + 三步法模板默写),以及第 21 章的 Student 类练习。
  • 补测通过后按"通过"记录,不影响进度;补测仍不过的,周一中午找老师单独过关。

考前 10 分钟速记(临阵磨枪):

  • 四要素 = 背景/角色 + 目标 + 输入输出 + 约束
  • 三步法 = 发生了什么 + 报错原文 + 我是初学者请给完整代码
  • 类四步 = class 类名:def __init__(self, ...) 装属性 → 方法里用 self.xxx对象 = 类名(参数) 创建并调用
  • 继承写法 = class 子类(父类):,子类方法里要父类帮忙就 super()