
说实话看到“python第五次作业”这个标题我第一反应就是又到了学期中段那种让人又爱又恨的节点。前四次作业可能还在练变量、循环、条件判断到了第五次老师通常就开始要求你把前面学的东西串起来写一个像样的完整程序。我当年接到的第五次作业是一个学生信息管理系统题目本身不算难但它特别适合用来检验你对函数、文件、异常处理和数据结构到底掌握到什么程度。如果你也正好卡在这次作业上或者只是想看看一个真实项目里Python代码是怎么组织起来的这篇内容应该能省你不少事。1. 作业内容整体拆解第五次作业到底想考你什么1.1 题面背后的隐藏考点函数拆分与代码组织第五次作业常见的形式就那么几种要么是学生/图书/商品信息管理系统要么是一个带菜单的小程序也有老师会布置贪吃蛇或“人狗大作战”这类小游戏。但不管题目怎么变核心考察点其实是同一件事你能不能把一个稍微完整的需求拆成函数去实现。前几次作业里你可能已经习惯了从上到下写一段脚本跑完就拉倒。但到了这个阶段程序开始有菜单、有循环、有数据保存如果还把所有逻辑堆在同一个while循环里代码很快就没法看了。我记得当时班里有个同学一个文件写了三百多行全在主函数里平铺加一个功能就要在main()里加好几层if最后改得满头大汗。所以拿到题面之后第一步不是急着敲代码而是先看它的功能清单。以学生信息管理系统为例核心功能大概就四个添加学生、删除学生、查找学生、显示所有学生再加一个退出。每个功能天然对应一个函数这是最基础也最合理的拆分方式。你甚至可以先把这些函数名写在注释里比如add_student()、remove_student()、search_student()再往里面填代码整体思路会清晰很多。另外这道作业的另一个隐藏考点是数据结构的选择。很多新手第一反应是用两个列表一个存姓名一个存成绩然后用索引对应。这样写勉强能做但一旦要删除中间某个人两个列表的下标就对不上了。我建议直接使用列表套字典的结构每个学生是一本“小档案”例如{id: 001, name: 张三, score: 88}所有学生再装进一个大列表。这样增删改查都只针对一个字典操作代码逻辑简单后面要加字段也方便。1.2 技术选型思路为什么文件存储选JSON而不是CSV很多同学的疑问是为什么不用CSV这得看你的需求。CSV的好处是可以用Excel直接打开适合纯表格数据。但CSV的解析和写入需要你来处理逗号、换行、引号这些细节虽然Python有内置的csv模块但相比JSON还是多了一层概念。JSONJavaScript Object Notation在Python里几乎是为“列表套字典”这种结构量身定做的。它能把整个列表直接序列化成一个字符串写进文件读文件的时候再反序列化回列表类型完全不会丢。代码量非常少import json def save_data(students, filenamestudents.json): with open(filename, w, encodingutf-8) as f: json.dump(students, f, ensure_asciiFalse, indent2) def load_data(filenamestudents.json): try: with open(filename, r, encodingutf-8) as f: return json.load(f) except FileNotFoundError: return []用CSV不是不行但意味着你要自己处理更多边界情况比如某个字段里如果出现了逗号要不要加引号。在这个阶段JSON明显是更贴合“写完作业”的选择。而且你之后如果是往爬虫、接口、数据分析这些方向走JSON基本是绕不开的早晚都得学这次作业正好是个简单的练习机会。2. 核心代码一步步拆开讲2.1 先搭框架主菜单循环和入口函数我不建议一开始就从第一个功能写起更好的顺序是先把整个程序的“骨架”搭出来也就是主菜单循环。这个循环决定了用户跟程序怎么交互一般长这样def main(): students load_data() while True: print(\n 学生信息管理系统 ) print(1. 添加学生) print(2. 删除学生) print(3. 查找学生) print(4. 显示所有学生) print(5. 退出) choice input(请输入你的选择1-5) if choice 1: add_student(students) elif choice 2: remove_student(students) elif choice 3: search_student(students) elif choice 4: show_all(students) elif choice 5: save_data(students) print(数据已保存再见) break else: print(输入无效请重新选择。)这个骨架有几个细节值得注意。第一students这个列表在程序启动时通过load_data()加载进来如果之前保存过数据程序重新启动后还能接着用这一下就把“数据持久化”的概念带出来了。第二在循环内部choice input(...)拿到的永远是字符串直接和1、2这些字符串比较就不需要再做类型转换也避免了输入非数字字符时int()抛异常的麻烦。第三退出分支里我先保存数据再break确保用户改动的数据不会丢。我给这个骨架再补充一句main()函数这个写法本身就是Python程序的标准入口。很多人刚开始不习惯写main()直接平铺在全局但一旦用了函数组织代码都会习惯性地把启动逻辑塞进main()然后在文件末尾写if __name__ __main__: main()。这样做的意义是如果这个文件被其他模块import不会一导入就立刻启动程序只有当成脚本直接运行时才会执行。这个知识点很基础但考勤、面试都爱问。2.2 核心函数实现增删改查和关键细节骨架搭好之后再往各个函数里填内容就舒服多了。先看添加功能。这里比较容易忽略的是“学号重复”的判断如果系统里已经存在某个学号你又插入同一条数据就会乱。def add_student(students): sid input(请输入学号) # 先检查学号是否已存在 for s in students: if s[id] sid: print(该学号已存在添加失败。) return name input(请输入姓名) score input(请输入成绩) # 这里可以把score转成float顺便做异常处理 try: score float(score) except ValueError: print(成绩格式不正确请输入数字。) return students.append({id: sid, name: name, score: score}) print(f学生 {name} 添加成功。)这里我用了try...except把成绩字符串转成浮点数。第五次作业的评分标准里异常处理经常是加分项因为老师在测试时一定会故意输入一个非数字看看程序会不会崩。提前兜住这种异常印象分能差不少。删除功能的常见写法是“输入学号找到就删找不到提示”。注意students.remove()这个方法要求你传入要删除的那个字典对象所以得先用for循环找到目标元素再调用remove()。还有个小细节如果你在for循环里找到了目标并且调用了删除一定要马上return或者break否则循环继续下去可能会出问题。更严谨一点可以配合一个found标志def remove_student(students): sid input(请输入要删除的学号) for s in students: if s[id] sid: students.remove(s) print(f学生 {s[name]} 已删除。) return print(未找到该学号的学生。)查找功能可以做得灵活一点支持按学号精确查也支持按姓名模糊查。模糊匹配用in判断就行比如if keyword in s[name]这样用户只输入半个姓名也能找到。显示所有学生就更简单了直接遍历列表打印。如果列表为空可以提示“暂无学生数据”否则手动加个输出格式。2.3 数据保存与读取JSON序列化的完整代码这一节其实在1.2里已经给过save_data和load_data的代码这里重点讲一下文件操作里的几个坑。第一encodingutf-8这个参数一定要写。Python默认的文件编码在不同操作系统上不一致Windows上有时默认是gbk如果你不指定写中文时可能报错或者写进去之后读出来是乱码。第二json.dump(students, f, ensure_asciiFalse, indent2)里的ensure_asciiFalse特别关键。如果不加中文会被编码成\u5f20\u4e09这样的Unicode转义序列虽然文件能读回来但用记事本打开看完全没法辨认。indent2是让JSON文件有缩进格式化之后阅读更舒服。读取的时候我用了try...except FileNotFoundError因为第一次运行程序时文件压根不存在这时直接返回一个空列表就好。这种“尝试打开打不开就给个默认值”的写法在后端开发里也常见叫做“容错处理”。不要觉得这是偷懒它恰恰是程序健壮性的体现。3. 实际写作业时踩过的坑3.1 文件路径相关运行代码却报FileNotFoundError很多人在自己的电脑上运行程序一切正常切换了目录或者用PyCharm跑的时候突然报错FileNotFoundError: [Errno 2] No such file or directory: students.json一脸懵。这大概率是当前工作目录跟你以为的目录不是同一个。这里要理解一个概念代码里写的相对路径students.json是相对于“当前工作目录”的而不是相对于代码文件所在目录。在终端里跑当前工作目录通常是你敲命令时所在的目录在IDE里跑可能是项目根目录也可能由IDE的运行配置决定。解决办法很简单要么把路径写成绝对路径要么在运行前先确认工作目录。最省心的做法是在开发阶段统一用绝对路径或者干脆用模块动态获取当前文件目录再拼接文件名import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) DATA_FILE os.path.join(BASE_DIR, students.json)这样不管你从哪里运行代码都能找到这个JSON文件。这个技巧放到真实项目里非常常用越早习惯越好。3.2 中文数据乱码或写入后变成\uXXXX写入JSON文件时中文变成\u5f20\u4e09或者读取时中文变成乱码这是第五次作业里出现频率最高的抱怨。原因就是我在上一节提到的写入时要加ensure_asciiFalse读写文件时都要带encodingutf-8。还有一个细节是如果你在Windows的记事本里打开生成的JSON文件看到的中文是正常的但用Excel打开CSV时又乱码了那是另一码事需要给CSV文件加utf-8-sig编码。这次作业如果用的JSON就不太会遇到这种二次乱码。总之记住一个原则Python处理文件中文字符统一在打开文件时显式指定编码不要靠系统默认值。3.3 函数修改列表却不起作用的迷思这个问题是初学者最容易绕晕的点。比如你在add_student(students)函数里明明写了students.append(...)主流程里打印students看起来也能看到新增的数据你觉得没问题。可一旦你改成students [...]重新赋值比如想“重置学生列表”往往会发现主流程里的列表根本没变。原因在于Python的参数传递是“引用传递”但函数内部如果对参数进行重新赋值比如students some_new_list那就只是让函数内部的局部变量students指向了另一个对象外部的原列表没受任何影响。而students.append()是对原来那个列表对象做“原地修改”所以外部能看到变化。为了理清这个概念我建议你在第五次作业时养成一个习惯尽量把数据作为参数传入并且在函数内做原地修改或者返回新的结果不要动不动用global声明全局变量。虽然第五次作业规模小用global也能跑但养成这个习惯后往后写更大项目时能少吃很多亏。3.4 常见问题速查表问题现象原因分析解决办法中文写入文件后变成\uXXXXjson.dump默认ensure_asciiTrue写入时加ensure_asciiFalse程序报FileNotFoundError相对路径指向了错误的工作目录用os.path.dirname(__file__)拼接绝对路径菜单输入字符串直接崩溃对输入做了int()但没有异常处理用字符串比较或try...except包住转换删除学生后数据刷新后还在删除操作只改了内存没调用保存退出前调用save_data(students)添加重复学号没有提示缺少查重逻辑遍历列表先判断id是否已存在函数内修改数据外部不变参数被重新赋值不是原地修改对列表用append/remove避免重新赋值这张表里的问题基本覆盖了我当年看到的、以及后来帮人改作业时遇到过的大部分情况。如果你写的是小游戏而不是管理系统同样适用只是数据结构和菜单内容不同罢了。4. 从第五次作业往外走下一步玩法4.1 给作业加一个可视化报告如果这次作业里已经有了成绩这个字段那加一个可视化报告是非常出彩的扩展。用matplotlib把成绩分布画成柱状图或者用饼图展示不同分数段的人数占比一下子就比普通同学的多了一个量级。import matplotlib.pyplot as plt def show_score_report(students): scores [s[score] for s in students] plt.hist(scores, bins5, edgecolorblack) plt.title(学生成绩分布图) plt.xlabel(分数) plt.ylabel(人数) plt.show()这段代码把学生的成绩列表提取出来用直方图展示分布。做这个扩展时你反而会发现自己得回头把数据结构再理一遍——因为要一次性拿到所有人的成绩之前“列表套字典”的结构就很好用了。如果你用的是散落的多个列表这一步会相当痛苦。4.2 把命令行程序改成Web接口第五次作业的另一个人气升级方向是把命令行程序改成一个带网页界面的服务。用Flask或者FastAPI把“添加学生”“删除学生”“查找学生”这些操作映射成HTTP接口你就能从网页或者接口调试工具里直接调用。这个方向的驱动力很直接现在的爬虫、后端、数据分析都离不开API。你提前把数据增删改查封装成函数再在Flask里调用这些函数你会发现“业务逻辑”和“接口层”分离的思路就是从这个规模很小的项目里开始的。比如add_student(students)本来是从命令行收输入改成Web接口后数据从请求体里拿其他部分几乎不用动。4.3 爬虫和数据分析的天然入口也有人会问这次作业跟爬虫有什么关系关系其实很大。爬虫的核心就是“发请求、拿数据、清洗结构、保存结果”最后一步保存结果时你往往要把抓下来的数据变成列表套字典的结构再写入JSON或CSV。如果你在第五次作业时已经熟练掌握了列表套字典和JSON读写爬虫的数据落地环节就畅通了。数据分析也是类似的逻辑。pandas读入的DataFrame本质上也是有行有列的二维表跟“学生列表”这种结构非常像。你想筛选出成绩大于90的学生、统计人数这些都和这次作业里的查找、遍历逻辑同出一源。所以我一直觉得第五次作业不是终点它是你从“学会语法”走向“能干活”的真正分水岭。我个人在写这个作业时体会最深的一件事是先别急着敲代码拿一张纸把“用户会怎么操作”这条线画出来。先输入什么、后输入什么、哪些数据要存、哪些数据要改顺着这个流程走一遍代码结构基本就出来了。我第一次没规划直接写写到一半发现增删改查里逻辑乱成一团后来重来一遍先列菜单、再列函数、最后填实现两个小时就搞定了。如果你也卡在“到底从哪下手”不妨试试这个方法。第五次作业真的不难难的是静下心来把思路理清楚。