
作为一个带过不少新手入门的开发者每次看到“Python第三次作业”这类标题都感觉特别亲切。因为这通常意味着你已经跨过了“print(Hello World)”和“if/else 试来试去”的新鲜期开始真正接触有逻辑、有结构、能落地的代码了。第三次作业往往是很多人口中的“分水岭”第一次是打基础第二次是练语法第三次就开始要求你把东西“串”起来用了。这篇文章就是写给正在为第三次作业挠头的朋友也写给所有刚开始接触Python、想知道“作业到底在考什么”的初学者我会从题目背后想考察的能力出发讲明白思路给出一套可以直接上手的实现再聊聊那些课堂上不常讲、但作业里一定会遇到的坑。1. 第三次作业到底在考什么1.1 一次典型的第三次作业长什么样根据我接触过的很多课程安排Python第三次作业通常会围绕“管理一个小型数据系统”之类的话题展开。比如最经典的就是写一个学生成绩管理系统或者图书借阅记录、员工信息管理、购物车结算功能。共同特点就三条有多次操作的循环交互、有数据的增删改查、有持久化保存的需求。举一个最常见的作业要求请用Python编写一个学生成绩管理系统支持录入学生姓名和成绩、修改成绩、删除记录、按成绩排序展示、从文件中保存和读取数据。乍一看要求不少但其实每个小点拆开都是很基础的代码能力。这个作业真正考察的根本不是“你会不会写某个算法”而是你能不能在同一个程序里把循环、函数、文件操作、列表字典这些知识组合在一起组成一个能真正跑起来的工具。1.2 老师设计这道题的底层逻辑很多初学者拿到题目就开始焦虑其实完全不用。你要理解设计这道题的老师在想什么。前两次作业是“点状”的考一个知识点第三次作业是“线状”的考的是一整条逻辑链。成绩管理系统这类题目设计得好是因为它天然由五个必须串联的环节构成程序运行后必须等待用户输入指令这需要while循环和input()配合。用户输入的每个指令都要对应不同操作这是if-elif-else分支判断。数据要能随时修改和删除这考验对列表和字典中元素操作的基本功。退出程序后再次打开时数据还在这需要文件读写能力。用户输入错了不能崩这涉及异常处理的意识。换句话说老师想看到的是“能不能独立完成一个小项目”的能力而不是“背会了多少个函数名”。你把这一点想明白了写作业的心态就完全不一样了。1.3 你需要具备的知识储备清单这里我列一个清单你在动手前先自测一下哪个知识点还不熟回头翻翻课本或资料补一遍比直接写代码效率高得多函数的定义与调用尤其是带参数、有返回值的函数。while循环的写法以及如何用break或一个flag变量控制循环退出。列表的append、remove、修改指定下标元素等操作。字典的键值对存取以及用.items()遍历。with open() ... as f实现文件读写以及编码参数encodingutf-8的意义。尝试用try...except包住容易出错的输入环节。我见过太多同学在这个阶段急着写大段代码结果写到一半发现连字典都不会遍历于是越写越乱。先跟着上面的清单自测一遍基础够不够自己心里就有数了。这跟盖房子之前先检查建材是一个道理不是浪费时间是帮你省掉后面的返工。2. 核心知识点拆解别急着写代码先搞懂这几件事2.1 函数你的代码从此有了“积木”第三次作业要求里的每个功能比如“添加记录”“修改成绩”“删除记录”“保存文件”如果把所有代码全部揉在main的while循环里一个if分支下面跟一两百行代码你很快就会被自己绕晕。这种时候函数就是你的救星。函数的本质是“给一段代码起个名字”。比如你想实现“修改成绩”就把这段逻辑提取成一个函数def modify_score(students): name input(请输入要修改成绩的学生姓名: ) if name in students: new_score int(input(请输入新的成绩: )) students[name] new_score print(修改成功) else: print(未找到该学生)这段代码单独写出来逻辑一目了然。到了主程序里只需要一行modify_score(students)就能调用。这样整个程序的main部分变得非常清爽每个分支只对应一个函数调用排查问题的时候也能快速定位。我个人特别建议你在写第三次作业时养成一个习惯先想清楚这个程序需要哪些行为然后根据行为设计函数而不是边写边想。哪怕是几行代码的简单功能也打包进函数里后面你维护起来会轻松得多。2.2 文件读写让程序记住上一次的样子第三次作业和前面最大的不同就是出现了“程序关闭后数据还在”这个需求。如果不做文件操作每次运行程序都是从头开始那这个管理系统实际上是没什么使用价值的。想明白这一点你就会理解为什么每个老师都非常强调文件读写。Python里的文件读写非常友好核心就是两行with open(students.txt, r, encodingutf-8) as f: data json.load(f) with open(students.txt, w, encodingutf-8) as f: json.dump(students, f, ensure_asciiFalse)这里有几个细节值得展开说一下。with open()的写法可以自动管理文件资源不管程序中途是否报错文件都会被正确关闭这比单独调用f.close()更安全。第二个参数r是读取模式w是写入模式写的时候要特别注意w会把原来的文件内容整个覆盖掉所以如果你要做“追加”效果得用a模式。很多同学第一次做文件操作时会遇到中文乱码的问题根源就是编码不一致。你在保存的时候用了默认编码读取的时候换了环境或者反过来就会出现“锟斤拷”之类的经典乱码。解决方案就是写入和读取都明确指定encodingutf-8。2.3 数据结构选型列表、字典还是两者结合写成绩管理系统这类作业最纠结的往往是数据怎么存。这个选择直接决定了后面所有代码的写法一定要想清楚。三种主流方案对比方案写法适合情况纯列表students [[张三, 90], [李四, 85]]按固定顺序遍历纯字典students {张三: 90, 李四: 85}按姓名查成绩列表套字典students [{name: 张三, score: 90}]需要扩展多个字段对于“按姓名修改成绩”这种操作纯字典是最直观的直接用if name in students判断名字在不在里面取成绩用students[name]改成绩用students[name] new_score都很自然。但如果以后要加学号、班级、性别这些字段纯字典就有点不够用列表套字典会更方便扩展。我的建议是如果作业没有硬性要求最简单、最不容易出错的就是方案。第一次做这种综合型作业不要追求数据结构的花哨能稳定地把所有功能实现你就已经完成了主要目标。等以后做更大的项目再来考虑数据结构的性能和扩展性。2.4 异常处理程序出错了也不能直接崩溃新手写程序有一个常见心理“只要我输入的都是对的程序就不会出错。”但真实世界里用户永远会给你“惊喜”。比如提示输入成绩结果输了一个“abc”程序直接报ValueError崩溃退出了。在作业里遇到这种情况轻则丢分重则被老师认为“程序健壮性不足”。异常处理的写法很简单try: score int(input(请输入成绩: )) except ValueError: print(输入格式有误成绩应该是一个数字)这段代码的意思是试着把输入转成整数如果失败了不要崩溃而是友好地提示用户重新输入。你甚至可以用一个while循环让用户输错了就一直要重新输直到正确为止这就成了一个完整的人机交互逻辑。对于第三次作业你不需要处理所有类型的异常只需要把最可能出错的“字符串转数字”这个环节管好就够了。但你要让老师看到你有这个意识这比把功能写完更宝贵。3. 手把手实操一个成绩管理系统的完整实现3.1 需求拆解与功能清单不管题目怎么描述做管理系统最重要的第一步就是把需求翻译成功能清单。就是把这个看起来很大的问题拆成一个个小到不能再小的动作。针对学生成绩管理系统我拆成了这样六个功能查看所有学生成绩添加一个学生记录修改一个学生的成绩删除一个学生的记录按成绩从高到低排序保存数据到文件并退出你注意一下这里的“保存数据到文件”被我拆成了“退出时自动保存”而不是单独做一个“保存”按钮。这么做的好处是用户无论怎么操作退出的时候数据都在不会因为忘了手动保存而丢失。这种设计上的小巧思往往能在作业里成为加分项。3.2 分步实现从主菜单到每个功能函数我先把完整代码框架贴出来然后逐段拆解。这个代码我特意加了足够多的注释方便你对照理解import json import os DATA_FILE students.json def load_data(): 程序启动时加载数据文件不存在就返回空字典 if os.path.exists(DATA_FILE): with open(DATA_FILE, r, encodingutf-8) as f: return json.load(f) return {} def save_data(students): 将数据保存到JSON文件 with open(DATA_FILE, w, encodingutf-8) as f: json.dump(students, f, ensure_asciiFalse, indent4) def show_menu(): 显示操作菜单返回用户选择 print(\n----- 学生成绩管理系统 -----) print(1. 查看所有学生) print(2. 添加学生) print(3. 修改成绩) print(4. 删除学生) print(5. 按成绩排序) print(0. 退出系统) return input(请选择操作: ) def show_all(students): 列出所有学生的姓名和成绩 if not students: print(当前没有学生记录) return print(\n姓名\t成绩) print(---\t---) for name, score in students.items(): print(f{name}\t{score}) def add_student(students): 添加一个新学生 name input(请输入学生姓名: ).strip() if not name: print(姓名不能为空) return if name in students: print(该学生已存在无法重复添加) return try: score int(input(请输入成绩: )) students[name] score print(f学生 {name} 添加成功) except ValueError: print(成绩必须是数字) def modify_score(students): 修改已有学生的成绩 name input(请输入要修改成绩的学生姓名: ).strip() if name not in students: print(未找到该学生) return try: new_score int(input(请输入新的成绩: )) students[name] new_score print(修改成功) except ValueError: print(成绩必须是数字) def delete_student(students): 删除一个学生记录 name input(请输入要删除的学生姓名: ).strip() if name not in students: print(未找到该学生) return confirm input(f确定要删除 {name} 吗(y/N): ) if confirm.lower() y: del students[name] print(删除成功) else: print(已取消删除) def sort_by_score(students): 按成绩从高到低展示所有学生 if not students: print(当前没有学生记录) return sorted_students sorted(students.items(), keylambda x: x[1], reverseTrue) print(\n按成绩从高到低排序:) for rank, (name, score) in enumerate(sorted_students, start1): print(f第{rank}名: {name} - {score}分) def main(): students load_data() while True: choice show_menu() if choice 1: show_all(students) elif choice 2: add_student(students) elif choice 3: modify_score(students) elif choice 4: delete_student(students) elif choice 5: sort_by_score(students) elif choice 0: save_data(students) print(数据已保存再见) break else: print(无效选择请重新输入) if __name__ __main__: main()这段代码总共120行左右。说实话不算长但很多人第一次写的时候就是写不出来因为代码的“骨架”没有在脑子里立起来。下面我逐段讲讲关键决策是怎么做的。load_data函数处理了一个很重要的场景第一次运行程序时文件根本不存在。如果没有os.path.exists这一步判断open(..., r)会直接报FileNotFoundError第一次启动就崩。很多同学就是在这个细节上栽了跟头。返回值用字典因为后面所有操作都围绕“姓名查成绩”字典是天然合适的数据结构。主程序的while循环是整个程序的发动机。choice show_menu()拿到用户输入的数字字符串后面的if-elif链条负责调度对应的函数。这个结构非常清晰哪怕以后要加功能只需要加一个分支和一个函数完全不需要动其他代码。你注意break在最后一个分支里面意思是退出前先保存数据。主程序末尾有一个if __name__ __main__:第一次见这段代码的人会觉得它是“模板咒语”其实它的作用是告诉解释器当这个文件被直接运行时调用main()如果这个文件被别的程序当成模块导入则不自动执行。这是一种很好的代码习惯哪怕现在用不上写下来也会养成专业意识。3.3 联调与测试自己先跑通一遍代码写完之后很多同学觉得“功能都有了应该没问题”直接交作业。我强烈建议你先自己按正常用户的思路把每个功能走一遍。我实测的时候一般会依次做这几步检查程序刚启动时没有students.json文件看程序能否正常显示空列表而不是崩溃。添加一个学生“张三 90”再添加一个“李四 85”然后查看所有学生看数据格式对不对。修改张三的成绩为95再查看确认修改生效。删除一个根本不存在的人看程序是否给友好提示。删除李四程序问要不要确认输入y后删除成功。退出程序重新打开看数据是否还在。我在测试这个版本时整个流程跑下来是稳定的。有两个地方特别容易出问题输入成绩的时候如果你手滑输了一个负数程序是接受的这在作业里一般不算大问题但如果想做得严谨一点可以再加一层“成绩必须在0到100之间”的校验删除确认的时候如果用户输入的是大写“Y”代码里已经用.lower()处理了所以大小写都不影响这个处理很多人一开始想不到。4. 写作业过程中最常踩的坑4.1 缩进与中英文符号问题Python在这一点上跟很多语言不一样它用缩进表示代码块的归属而不是大括号。这导致两个最常见的问题第一Tab和空格混用。你如果在一个代码块里一会儿按Tab一会儿按四个空格Python解释器会直接报IndentationError因为它在部分版本里把Tab和空格看作是两种不同的缩进。第二标点符号全用成了中文的比如写函数定义时用了中文括号def f解释器会报语法错误你还盯着看了半天没看出来。我自己排查这类问题有一个小技巧用支持语法高亮的编辑器打开代码比如VS Code、PyCharm缩进错误会直接显示成红色波浪线中文标点通常也能一眼看出来。如果你真的在命令行里调试报错信息里会明确提示第几行出错把光标移过去看绝大部分都是这两类小问题。4.2 文件读写与乱码问题文件读写相关的坑非常经典。你可能会遇到这几个场景程序第一次运行正常第二次运行报“file not found”或者明明保存了数据重新打开却看到一堆乱码又或者打开文件后发现数据挤在一行里完全没法看。这些问题的根源都在于对open函数的模式和编码理解不够清楚。对于“file not found”的问题核心是你要知道程序运行的“当前目录”是哪里。如果你是直接在命令行里运行python main.py当前目录就是main.py所在的目录。如果你在PyCharm里点运行按钮当前目录可能是项目根目录不一定和代码文件在同一个地方。这里我给一个最稳妥的解决办法把文件名写成一个常量放在程序顶部比如DATA_FILE students.json然后确保代码文件和运行环境一致避免在代码里写一串很长的绝对路径因为换一台电脑跑可能就失效了。乱码问题前面提过就是编码不一致导致的。保存和读取都用encodingutf-8就能解决。4.3 逻辑边界问题功能测试时你可能会发现一个有意思的现象有些问题不是报错而是“行为不符合预期”。比如你查询一个不存在的学生程序没有给提示安静地什么都不做或者排序后原来的顺序乱了你下次再查看又变回原来的顺序了。这些都属于逻辑边界问题。处理“查不到”这类情况最核心的原则是每次通过input拿到一个名字先验证它是否存在于数据结构中存在才做下一步操作不存在就返回友好提示。我在前面的代码里每个函数都做了这个判断比如modify_score里的if name not in students: print(未找到该学生); return都是一样的套路。排序那里还有一个细节sort_by_score函数内部使用了sorted(students.items(), keylambda x: x[1], reverseTrue)这不会改变原有字典的顺序只是在展示时临时排好序。这个设计是有意为之因为如果你真的把原有的字典顺序改了用户下次查看时看到的是排序后的结果反而会觉得很奇怪。5. 从“能运行”到“有亮点”5.1 加一个简单的菜单界面基础的作业要求做到“能运行”就已经合格了但如果你想拿高分或者单纯想让自己的代码更有层次感有几个小方向可以努力。第一个就是界面优化。现在很多人早就用上了图型界面但基于终端的交互也可以做得很舒服不需要第三方库只要用好Python的字符串格式化就行。比如在show_menu函数里你可以用print(- * 30)画一条分隔线或者用三引号字符串把菜单排版成居中的样式。这虽然不影响功能但会给看代码的人一个很好的第一印象这个人是有审美的。5.2 数据校验与优雅提示第二亮点方向是数据校验。我刚才提到成绩如果是负数或者大于100从逻辑上是不合理的。你可以在输入成绩后加一个判断if score 0 or score 100: print(成绩应在0到100之间) return再比如学生的姓名如果全是空格或者是一个极长的字符串也可以在录入时就拦截掉。这些设计体现了“防御性编程”的思维在真实开发中非常重要。老师看到你的代码能处理这些边界情况印象分会明显不一样。5.3 把代码整理出清晰的模块结构第三个方向是代码结构的优化。如果你觉得把六个函数全部放在一个文件里有点拥挤可以考虑按功能拆成两个文件一个管交互一个管数据处理。比如新建一个storage.py专门放load_data和save_data在主文件里用from storage import load_data, save_data导入。这一步会让你的作业从“大一新生的水平”直接跳到“有工程素养的初级开发者水平”。最后再分享一个我的建议写完作业后抽点时间把你的代码从头到尾注释一遍。不是为了交作业而是为了逼自己搞清楚每一行在干什么。我见过太多人CtrlC和CtrlV拿到了别人的代码运行成功就交差结果一问三不知。注释一遍之后你会发现有些地方你自己也讲不明白那一刻就是你真正开始学会编程的时刻。这也是我做Python项目这么多年最受益的一个习惯。