恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Python学生管理系统课程设计:从CRUD到exe打包全解析
首页
资讯中心
/
Python学生管理系统课程设计:从CRUD到exe打包全解析
Python学生管理系统课程设计:从CRUD到exe打包全解析
发布时间:2026/9/16 2:42:00
简介面向Python课程设计任务的学生管理系统完整方案适合初学Python编程或需要完成信息管理类课程设计的本科、专科学生使用。资源压缩包约5.96MB主要包含Python源码、可直接运行的exe程序以及详细使用说明文档三类文件无需手动搭建复杂环境即可查看演示效果也能对照源码理解系统实现逻辑。系统实现了学生信息的录入、查找、删除、修改、排序、总人数统计、显示所有信息和退出共八项功能覆盖日常教务管理常见场景可作为课程设计报告撰写、功能模块划分和答辩演示时的参考基础。已有767人学习下载压缩包内文件类型清晰源码具备一定注释便于在原有功能上做二次开发与扩展是完成学生管理类课题时值得借鉴的完整样例。1. 为什么一个学生管理系统被反复当课程设计拆开才看懂“学生管理系统”是Python课程设计里最常见的题目常见到很多人觉得没有技术含量但真到自己从零写往往会在菜单循环、文件读写、异常处理上卡一晚上。这套东西表面上是八个菜单功能实际涉及了命令行交互、列表字典组合、函数分发、数据持久化、边界条件处理。更难得的是这份资源里还附带了直接能运行的exe程序也就是说不需要先装Python环境就能跑起来看效果然后对照源码一步步改。对准备交课程设计的人来说它是可以直接落地的作业对已经工作几年、想回忆一下命令行程序设计逻辑的人来说它是很好的“白盒测试对象”。我拿到后先双击exe跑了一圈再打开源码读结构发现有几个地方比网上常见的demo做得更完整也藏着几个值得注意的坑。2. 菜单分发与json数据层把功能边界先画清楚2.1 主循环用while True和函数映射替代连环if-else所有功能都挂在while True主循环下用户输入数字后跳转到对应函数。源码里最常见的写法是长长的if-elif链功能只有8个还好但如果以后要加“按班级筛选”、“导出成绩表”这条链子会越来越难维护。我更建议的是用一种“事件表”结构把菜单选项和函数引用存进字典# 菜单选项对应的函数指针表 menu_actions { 1: add_student, 2: search_student, 3: delete_student, 4: modify_student, 5: sort_students, 6: count_students, 7: show_all_students, 8: exit_system, }主循环里用dict.get做安全分发while True: show_menu() raw input(请输入选择(1-8): ).strip() if not raw.isdigit(): print(请输入数字) continue choice int(raw) action menu_actions.get(choice) if not action: print(选项不存在) continue if choice 8: break action()逻辑说明raw.isdigit()先过滤掉非数字输入避免后面int()抛异常。menu_actions.get(choice)在key不存在时返回None所以能统一处理越界输入。选择8时直接break否则调用对应函数。这个结构比一大串elif清晰后续加功能也只需要在字典里多挂一项不用动主循环。参数说明menu_actions的值是函数对象不是函数调用字符串所以不能写成add_student。 在Python里函数是一等对象可以直接被赋值给变量或存进容器这也是课程设计里最容易忽略的设计点。2.2 学生数据模型列表套字典而不是几个平行列表源码里的学生数据基本是这个样子student { sid: 2024001, name: 陈晨, python_score: 88.0, math_score: 91.0, } students [student, ...]每个学生是一个字典所有学生放在一个列表里。为什么不用四个平行列表分别存学号、姓名、成绩因为删除时四个列表要同步删除漏一个就会导致数据错位。字典的好处是字段名直接把数据和业务含义绑定排序时能用keylambda s: s[python_score]直接取值不用记住下标。提示 字典键名尽量全英文和下划线不要用中文“姓名”虽然Python3支持中文变量但打包exe时偶尔会遇到编码问题字符串内部的中文不影响键名这种“代码级标识符”还是保持英文更稳妥。2.3 文件持久化用json保证下次启动数据还在一个只能运行时操作的课程设计不算完整重启后数据还在才是真正能用。常见做法是把students写入json文件import json DATA_FILE students.json def load_data(): try: with open(DATA_FILE, r, encodingutf-8) as f: return json.load(f) except FileNotFoundError: return [] def save_data(students): with open(DATA_FILE, w, encodingutf-8) as f: json.dump(students, f, ensure_asciiFalse, indent2)逻辑说明load_data捕获FileNotFoundError首次运行没有json文件时返回空列表程序不会启动即崩溃。save_data每次增删改后都会全量覆写文件对这种课程设计规模的数据量来说完全够用不需要做增量写入。参数说明ensure_asciiFalse必须保留。如果不加json.dump会把中文转成\u9648\u6668这种ASCII转义序列文件里全是反斜杠非常难看而且重新加载后数据内容虽然不变但调试和手动改数据都会崩溃。indent2是让json输出有缩进方便用编辑器直接检查数据。2.4 json、csv、pickle怎么选格式优点缺点json结构完整保留嵌套关系文本可读默认中文转义需要设置ensure_asciiFalsecsvExcel直接打开适合做成绩单嵌套字典处理麻烦编码问题多pickle序列化速度快Python原生支持二进制不可读不能跨Python版本保证兼容存在安全风险课程设计场景我强烈建议json。它既保留了Python数据类型的嵌套关系又能用任何文本编辑器打开检查。csv对人友好但对嵌套数据结构极其不友好pickle则完全不适合提交作业老师没法打开看数据到底存了什么。3. CRUD实战从增删改查到排序统计手写一遍才过瘾3.1 录入学生先查重再保存录入功能看起来简单真正的坑是学号重复。网上很多demo都是直接append结果同一个学号能存好几遍。成熟一点的写法是def add_student(): sid input(请输入学号: ).strip() if any(s[sid] sid for s in students): print(学号已存在不能重复添加) return name input(请输入姓名: ).strip() try: py_score float(input(请输入Python成绩: )) except ValueError: print(成绩必须是数字) return students.append({ sid: sid, name: name, python_score: py_score, }) save_data(students) print(添加成功)逻辑说明any(s[sid] sid for s in students)遍历列表检查是否已有相同学号。这里用了生成器表达式遇到相同学号会提前短路数据量大时比先构建一个list再判断要省内存。输入成绩时用float()抓异常防止用户输入“90分”这种带中文的内容导致崩溃。参数说明.strip()去掉输入两端的空格否则你录入“ 2024002”和“2024002”系统会认为是两个不同学号。这是一个看起来很细但排错时要查半天的点。3.2 查找学生精确匹配和模糊匹配一起做查找功能一般支持学号精确查、姓名模糊查。代码里通常是这样def search_student(): keyword input(输入学号或姓名关键字: ).strip().lower() found [ s for s in students if s[sid] keyword or keyword in s[name].lower() ] if not found: print(没有找到匹配的学生) return for s in found: print(f{s[sid]} {s[name]} Python: {s[python_score]})逻辑说明学号用精确匹配姓名用in做子串匹配这样输入“陈”也能找到“陈晨”。.lower()把关键字和姓名都转成小写避免“CHEN”和“陈”这种中西文混搭的问题虽然姓名很少有大写但统一处理没有坏处。参数说明in是Python的子串判断操作符chen in chenchen返回True。如果学生很多这个线性匹配会变慢但课程设计几百个学生完全不用考虑索引优化。3.3 删除与修改先定位索引再操作列表删除的初级写法是在for循环里调用list.remove()这在删除第一个匹配项时没问题但如果你在遍历同一个列表时连续删除多个元素因为列表长度实时变化元素会往前挤你会跳过某些没检查到的对象。稳妥的做法是def delete_student(): sid input(请输入要删除的学号: ).strip() for idx, s in enumerate(students): if s[sid] sid: del students[idx] save_data(students) print(删除成功) return print(学号不存在)逻辑说明enumerate(students)返回(下标, 元素)这样的元组直接在循环里通过下标删除。删除完成后立即return避免继续遍历已经修改过的列表。这个方案比先记下标再统一删除更直观也避免了索引错位。修改信息的逻辑类似多了一个“更新字段”的环节def modify_student(): sid input(请输入要修改的学号: ).strip() for s in students: if s[sid] sid: new_name input(f新姓名({s[name]}, 直接回车保持不变): ).strip() if new_name: s[name] new_name new_score input(新Python成绩(直接回车保持不变): ).strip() if new_score: try: s[python_score] float(new_score) except ValueError: print(成绩格式错误本次修改取消) return save_data(students) print(修改完成) return print(学号不存在)这段代码里有个技巧input(prompt)的提示语里使用f-string直接显示当前值用户直接回车就保留原值。if new_name:判断空字符串因为.strip()会去掉空白输入空格再回车也会被判定为空。成绩输入部分如果格式错误就整个取消本次修改避免数据被写一半。3.4 排序key函数和reverse的配合别搞反排序是“学生管理系统”的需求重头。源码里一般会提供按学号升序、按成绩降序两种模式def sort_students(): print(按学号排序请按1) print(按Python成绩排序请按2) choice input(请选择: ).strip() if choice 1: sorted_list sorted(students, keylambda s: s[sid]) elif choice 2: sorted_list sorted(students, keylambda s: s[python_score], reverseTrue) else: print(无效选择) return for s in sorted_list: print(f{s[sid]} {s[name]} {s[python_score]})逻辑说明key是一个函数它接收列表里的一个元素返回用于比较的值。这里用lambda表达式取出字典里的字段。reverseTrue表示降序成绩排序用降序因为大家希望看到最高分在前面。如果是按学号排序用升序更符合习惯。一个容易被忽略的细节sorted()不会修改原列表而是返回一个新的列表。所以排序后再执行录入、查找数据顺序还是你原来的追加顺序。这其实是个好特性但有人误以为执行排序后列表已经变了发现没变时会以为代码有bug。如果希望成绩相同的学生再按学号升序排key可以返回一个元组sorted_list sorted(students, keylambda s: (-s[python_score], s[sid]))参数说明元组比较时先比较第一个元素相等时比较第二个。-s[python_score]是把分数取负数这样原分数高的反而排前面实现“按成绩降序、成绩相同学号升序”。直接用负数取巧是最简洁的写法之一。3.5 统计总人数和显示所有学生统计总人数一行就够了def count_students(): print(f当前共有 {len(students)} 名学生)显示全部学生时注意使用格式化字符串保证对齐def show_all_students(): print(学号\t姓名\tPython成绩) for s in students: print(f{s[sid]}\t{s[name]}\t{s[python_score]})\t是制表符能让表格在控制台里勉强对齐。如果名字长度不一样想对齐得更精细可以用f{s[name]:8}表示左对齐占8位。这个细节在答辩演示时会显得很专业。4. 源码到exePyInstaller参数选择和命令行程序的打包坑4.1 为什么课程设计最好附exe交作业时老师不一定在每台电脑上装Python装了也不一定有正确的环境变量。打包exe之后双击就能运行省去大量“跑不起来”的沟通成本。打包工具我用得最多的是PyInstaller它能把Python解释器、依赖库、源码都打进去。先安装pip install pyinstaller然后进入源码目录执行pyinstaller -F student_manager.py-F是--onefile的缩写把所有东西打进一个exe文件。不加这个参数会生成一个目录里面有一堆dll和文件虽然启动快但不方便拷贝交作业。4.2 命令行程序千万不要加 -w 参数网上很多打包教程会推荐-w它的作用是“隐藏控制台窗口”适合带GUI的Tkinter或PyQt程序。但学生管理系统是纯命令行交互的如果你打包时加了-w双击exe后窗口会一闪而过因为程序在等待input输入而控制台被隐藏了用户什么都看不到。正确命令是pyinstaller -F student_manager.py如果你已经给程序加了精美图标可以追加-i app.icopyinstaller -F -i app.ico student_manager.py这里-F和-i可以组合使用顺序无所谓。需要注意图标必须是.ico格式直接把.png改后缀是不行的PyInstaller会报错。可以用在线转换工具生成ico或者用pillow转换from PIL import Image img Image.open(app.png) img.save(app.ico, sizes[(16,16), (32,32), (64,64)])参数说明sizes指定内置的多尺寸图标Windows在桌面显示大图标或任务栏小图标时会自动选择最接近的尺寸。4.3 打包后闪退或无响应的排查手段拿到exe后先别双击在dist目录里按下shift右键打开PowerShell窗口直接输入exe文件名运行.\student_manager.exe这样一来如果程序有Traceback报错就会直接输出到当前窗口而不是闪退导致什么都看不到。常见的错误如下现象原因解决办法双击闪退程序里某个文件路径不对或缺少依赖用PowerShell运行看报错信息中文乱码源码文件不是UTF-8编码Windows终端是GBK源码顶部加# -*- coding: utf-8 -*-并另存为UTF-8提示找不到students.json程序使用相对路径exe启动后工作目录不对在代码里写绝对路径或使用os.path.dirname(sys.executable)拼接路径杀毒软件误报PyInstaller打包的exe被误判可以尝试加--uac-admin或向杀毒软件提交信任不影响功能最值得注意的坑是数据文件路径。源码在同目录下用open(students.json, r)没问题包成exe后你双击它当前工作目录不一定是exe所在目录而是该快捷方式或资源管理器所在的启动位置。一个简单的修复方式是在代码里把数据文件路径改成“exe同目录下的固定位置”import os import sys if getattr(sys, frozen, False): BASE_DIR os.path.dirname(sys.executable) else: BASE_DIR os.path.dirname(__file__) DATA_FILE os.path.join(BASE_DIR, students.json)逻辑说明sys.frozen是PyInstaller打包后特有的属性在正常Python脚本中不存在。通过判断该属性可以决定程序运行时的基准目录。sys.executable指向exe的完整路径取它的目录名就能保证数据文件写在exe旁边不会跑到别的地方。4.4 用spec文件固定打包参数PyInstaller第一次执行后会在源码目录生成.spec文件里面记录了打包参数。如果你要多次打包直接改spec再执行pyinstaller student_manager.spec比打一长串命令更可靠。pyinstaller -F student_manager.py生成的student_manager.spec关键部分类似这样a Analysis( [student_manager.py], pathex[], binaries[], datas[(students.json, .)], hiddenimports[], ... ) exe EXE( pyz, a.scripts, a.binaries, a.datas, namestudent_manager, consoleTrue, iconapp.ico, ... )参数说明datas是一个列表每一项是(源文件路径, 目标目录)。把students.json放进去相当于把默认数据文件打包进exe用户解压运行时程序自行提取到临时目录。但要注意如果用临时目录里的json用户修改的数据不会持久化到exe旁边所以最稳妥的还是第四章里“exe同目录”方案。consoleTrue表示显示控制台窗口命令行程序必须设为True。5. 把课程设计变成能塞进简历的作品三个改动立刻提升质感5.1 拆文件把自己从“一个文件大师”变成“工程结构清晰”课程设计最常见的样子是一个main.py里堆了十来个函数看完之后很难快速定位修改点。我一般会拆成至少三个模块models.py放数据结构和校验storage.py放读写jsonui.py放菜单和交互。目录结构可以是student_manager/ ├── main.py ├── models.py ├── storage.py ├── ui.py └── requirements.txtmain.py只做两件事初始化数据和调用界面循环。别的模块不要互相越权调用。5.2 写一个通用的输入校验函数替换到处try/except在课程设计里成绩输入的校验被散落在录入和修改两处以后还想加“平时成绩”和“期末成绩”就会越写越乱。把校验抽出来def input_score(prompt): while True: raw input(prompt).strip() try: score float(raw) except ValueError: print(请输入数字) continue if 0 score 100: return score print(成绩范围是0-100)这个函数的作用是只要它没有return就会一直循环要输入。调用方再也不用关心成绩格式问题。后续如果增加实验成绩输入范围变成0-50可以给函数加一个max_score参数或者用lambda定制但现在的结构对课程设计已经足够。5.3 用unittest.mock模拟用户输入让验收自动化答辩演示时手动输入一遍很费时间而且容易输错。你可以写一个测试文件来模拟一连串的输入。unittest.mock.patch可以在测试期间把builtins.input替换成固定返回序列的对象from unittest.mock import patch def test_add_and_count(): with patch(builtins.input, side_effect[2024001, 王五, 88]): add_student() assert len(students) 1 with patch(builtins.input, side_effect[1]): count_students()参数说明side_effect是一个列表时每次调用input会依次返回列表中的元素。这样你不用开着窗口手动测试直接跑pytest就能验证功能。更进一步你可以在测试结束后删除测试数据避免污染真正的students.json。写到这一步你已经不是在交一个“学生管理系统”作业而是在展示自己对命令交互、数据持久化、代码组织、异常处理、自动测试的整体理解。把这些细节做扎实再单独写一个export_csv()函数把学生信息导出成CSV给老师看Excel打开就是整齐的表格。代码很短却比贴截图更有说服力。本文还有配套的精品资源点击获取