Python学生成绩管理系统:SQLite+Excel工程实践指南

发布时间:2026/10/12 16:56:20
Python学生成绩管理系统:SQLite+Excel工程实践指南
简介本资源是一套完整的基于Python的学生成绩管理系统项目源码与配套文档面向Python初学者、软件工程实践者及高校课程设计学生解决教学场景中成绩录入、查询、统计与可视化等核心管理需求。压缩包共412个文件约12.3MB包含39个核心Python源文件含业务逻辑与数据库交互、41个Vue前端组件实现响应式管理界面、68个pyc编译文件、32张JPG/PNG界面截图及操作说明图另有bat批处理脚本install/run/build支持一键环境部署以及SVG图标、JS/CSS/SCSS样式资源和基础配置文件。已有290人学习下载资源结构清晰前后端分离明确涵盖需求分析、数据库设计含学生/课程/成绩三表关系、Flask/Django风格后端实现、Vue前端交互及完整用户手册可直接运行调试亦可作为课程设计范本、毕业设计参考或Python全栈开发入门实战素材。1. 为什么一个“学生成绩管理系统”值得用 Python 重写三遍——不是练手是验证你能不能把需求真正落地你见过太多“Python 学生成绩管理系统”的 GitHub 仓库界面简陋、数据全在内存里、增删改查一跑就崩、导出 Excel 乱码、连班级平均分都算错。这不是代码水平问题而是从需求理解到工程落地的断层——学生要查自己各科排名老师要导出 PDF 成绩单教务要按学期统计及格率而你写的系统只支持“输入姓名→回车→显示所有成绩”。真正的学生成绩管理系统本质是多角色权限结构化存储可审计操作可扩展报表的最小闭环。它不考验算法深度但极度暴露工程直觉文件路径怎么组织才不被覆盖学号重复时是报错还是自动去重删除学生后他关联的考试记录要不要级联清理本篇不讲 tkinter 美化技巧也不堆砌 Flask 路由而是带你用纯 Python3.8 SQLite CSV/Excel 双导出从零搭起一个能进真实教室、能接班主任日常、能经得住期末批量导入导出压力的系统。适合刚学完函数和类、正卡在“写完代码却不敢给老师用”阶段的开发者。后面每一步我都踩过坑、改过三次、压测过 2000 条记录。2. 用 SQLite 做底层为什么不用 JSON 或 CSV 直存——数据一致性才是第一道生死线2.1 为什么 SQLite 是这个场景的“最优解”而不是“将就选”新手常犯的错误是把学生成绩存成students.json或scores.csv。表面看简单json.dump()一行搞定pandas.read_csv()两行读取。但真实场景中你会立刻撞上三个黑匣子并发写入冲突班主任 A 正在录入张三数学成绩班主任 B 同时修改张三语文成绩 → CSV 文件被覆盖数学成绩丢失数据类型失控CSV 里“85.5”和“缺考”混在同一列后续计算平均分时float(缺考)直接报错关联查询无力要查“高二3班数学≥90 分的学生名单”CSV 需手动遍历全部行时间复杂度 O(n)2000 条记录就要 3 秒以上。SQLite 则天然解决这三点✅ 单文件嵌入式数据库无需独立服务进程pip install pysqlite3即装即用✅ 支持 ACID 事务BEGIN TRANSACTIONCOMMIT保证多操作原子性✅ 原生支持INTEGER,REAL,TEXT,DATE类型建表时强制约束字段语义✅ 内置JOIN,GROUP BY,HAVING一条 SQL 就能完成跨表统计。提示不要用sqlite3模块的默认isolation_levelNone自动提交它会让INSERT后立即生效失去事务控制能力。必须显式开启事务。2.2 创建四张核心表字段设计背后的业务逻辑系统需支撑“学生-班级-课程-成绩”四维关系。我按真实教务逻辑设计表结构每个字段名都对应一个明确业务含义拒绝col1,data2这类命名-- 学生主表唯一标识靠 student_id非学号学号可能重复或变更 CREATE TABLE students ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id TEXT UNIQUE NOT NULL, -- 校内唯一学号如 2023001 name TEXT NOT NULL, gender TEXT CHECK(gender IN (男, 女, 其他)), class_id INTEGER NOT NULL, -- 关联班级表 enrollment_date DATE DEFAULT CURRENT_DATE ); -- 班级表避免在学生表里硬编码班级名如高二3班便于后期调整 CREATE TABLE classes ( id INTEGER PRIMARY KEY AUTOINCREMENT, class_code TEXT UNIQUE NOT NULL, -- 如 G2-03程序内用不对外展示 class_name TEXT NOT NULL, -- 如 高二3班前端显示用 grade_level TEXT NOT NULL, -- 高一/高二/高三 advisor TEXT -- 班主任姓名 ); -- 课程表同一门课不同学期可能有不同编号如数学-2024秋 vs 数学-2025春 CREATE TABLE courses ( id INTEGER PRIMARY KEY AUTOINCREMENT, course_code TEXT UNIQUE NOT NULL, -- 如 MATH-2024Q3 course_name TEXT NOT NULL, -- 如 数学 semester TEXT NOT NULL, -- 2024-2025学年第一学期 credit REAL DEFAULT 4.0 ); -- 成绩表核心业务表外键强约束避免孤儿数据 CREATE TABLE scores ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id TEXT NOT NULL, course_id INTEGER NOT NULL, score REAL CHECK(score 0 AND score 100 OR score IS NULL), -- 允许空值未考 exam_type TEXT DEFAULT 期中 CHECK(exam_type IN (期中, 期末, 随堂, 补考)), recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (student_id) REFERENCES students(student_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE RESTRICT );关键设计点说明students.student_id设为TEXT UNIQUE而非INTEGER—— 实际学号含字母如S2023A001或前导零00123用整数会丢失精度scores.score的CHECK约束明确允许NULL表示未参加考试比用-1或缺考字符串更符合 SQL 规范后续AVG()自动忽略NULLON DELETE CASCADE在删除学生时自动清理其成绩但ON DELETE RESTRICT阻止误删课程否则所有该课成绩变孤儿 所有TEXT字段未设NOT NULL的如advisor因班主任可能暂未确定留空比填待定更利于后续查询过滤。2.3 初始化数据库用 Python 脚本一键建库插测试数据把上面 SQL 封装成init_db.py执行一次生成完整结构# init_db.py import sqlite3 import os DB_PATH school.db def init_database(): if os.path.exists(DB_PATH): print(f警告{DB_PATH} 已存在将被覆盖) os.remove(DB_PATH) conn sqlite3.connect(DB_PATH) cursor conn.cursor() # 创建表SQL 字符串直接粘贴自上文 cursor.executescript( CREATE TABLE students (...); -- 此处省略实际填入完整建表语句 CREATE TABLE classes (...); CREATE TABLE courses (...); CREATE TABLE scores (...); ) # 插入测试班级避免空班级导致前端报错 cursor.execute(INSERT INTO classes (class_code, class_name, grade_level, advisor) VALUES (?, ?, ?, ?), (G2-03, 高二3班, 高二, 王老师)) # 插入测试课程 cursor.execute(INSERT INTO courses (course_code, course_name, semester, credit) VALUES (?, ?, ?, ?), (MATH-2024Q3, 数学, 2024-2025学年第一学期, 4.0)) conn.commit() conn.close() print(✅ 数据库初始化完成共创建 4 张表) if __name__ __main__: init_database()运行python init_db.py后你会得到一个 16KB 的school.db文件。用 DB Browser for SQLite 打开能看到四张表已就位且classes和courses中已有测试数据——这是后续所有功能的基石。没有这一步后面所有增删改查都是空中楼阁。3. 核心业务逻辑封装把“添加学生”变成可复用、可测试、可回滚的操作3.1 定义 StudentManager 类为什么不用全局函数而要用类封装很多教程把所有功能写成独立函数add_student(),delete_student(),get_class_avg()……看似简单但实际项目中会迅速失控❌ 函数间共享状态靠全局变量多线程下数据错乱❌ 每次操作都要重新connect()/close()连接泄漏风险高❌ 无法统一处理异常如学号重复时有的函数print(已存在)有的直接sys.exit(1)。正确做法是定义StudentManager类把数据库连接作为实例属性所有操作复用同一个连接并内置事务管理# manager.py import sqlite3 from datetime import datetime from typing import List, Dict, Optional class StudentManager: def __init__(self, db_path: str school.db): self.db_path db_path # 连接复用避免频繁 open/close self.conn sqlite3.connect(db_path) self.conn.row_factory sqlite3.Row # 支持字典式取值row[name] def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: self.conn.commit() else: self.conn.rollback() self.conn.close() def add_student(self, student_id: str, name: str, gender: str, class_code: str) - bool: 添加学生自动关联班级ID try: # 1. 先查班级是否存在 class_id self._get_class_id_by_code(class_code) if class_id is None: raise ValueError(f班级 {class_code} 不存在) # 2. 插入学生利用 REPLACE INTO 实现“存在则更新不存在则插入” # 注意此处用 REPLACE 而非 INSERT OR IGNORE因需确保 class_id 正确 self.conn.execute( INSERT OR REPLACE INTO students (student_id, name, gender, class_id) VALUES (?, ?, ?, ?), (student_id, name, gender, class_id) ) return True except sqlite3.IntegrityError as e: if UNIQUE constraint failed in str(e): print(f❌ 学号 {student_id} 已存在) else: print(f❌ 数据库约束错误{e}) return False except Exception as e: print(f❌ 添加学生失败{e}) return False def _get_class_id_by_code(self, class_code: str) - Optional[int]: 私有方法根据班级编码查ID避免重复SQL cursor self.conn.cursor() cursor.execute(SELECT id FROM classes WHERE class_code ?, (class_code,)) row cursor.fetchone() return row[0] if row else None def get_student_scores(self, student_id: str) - List[Dict]: 获取某学生所有成绩含课程名和学期 cursor self.conn.cursor() cursor.execute( SELECT s.id, s.student_id, s.name, c.course_name, c.semester, sc.score, sc.exam_type, sc.recorded_at FROM students s JOIN scores sc ON s.student_id sc.student_id JOIN courses c ON sc.course_id c.id WHERE s.student_id ? ORDER BY c.semester DESC, sc.recorded_at DESC , (student_id,)) return [dict(row) for row in cursor.fetchall()]关键设计说明__enter__/__exit__实现上下文管理确保with StudentManager() as mgr:退出时自动 commit 或 rollback_get_class_id_by_code是私有方法避免在add_student和update_score中重复写相同 SQLINSERT OR REPLACE替代INSERT OR IGNORE当学号已存在时会更新name,gender,class_id字段适应转班、更名等场景get_student_scores使用JOIN一次性查出学生、课程、成绩三表关联数据前端直接渲染无需多次查询。3.2 添加成绩如何处理“同一学生同一课程多次考试”教务常见需求张三本学期考了两次数学期中期末不能覆盖要并存。scores表设计时已预留exam_type字段现在实现add_score方法def add_score(self, student_id: str, course_code: str, score: float, exam_type: str 期中) - bool: 添加成绩自动关联课程ID try: # 1. 验证学生存在 cursor self.conn.cursor() cursor.execute(SELECT 1 FROM students WHERE student_id ?, (student_id,)) if not cursor.fetchone(): raise ValueError(f学生 {student_id} 不存在) # 2. 获取课程ID cursor.execute(SELECT id FROM courses WHERE course_code ?, (course_code,)) course_row cursor.fetchone() if not course_row: raise ValueError(f课程 {course_code} 不存在) course_id course_row[0] # 3. 插入成绩允许同一学生同一课程多条记录 self.conn.execute( INSERT INTO scores (student_id, course_id, score, exam_type) VALUES (?, ?, ?, ?), (student_id, course_id, score, exam_type) ) return True except ValueError as e: print(f❌ {e}) return False except Exception as e: print(f❌ 添加成绩失败{e}) return False⚠️ 注意这里没用REPLACE INTO因为成绩是追加行为不是覆盖。INSERT本身允许重复student_idcourse_id组合只要exam_type不同符合业务。3.3 批量导入用 CSV 文件一次导入 500 名学生班主任常提供 Excel 表你得支持 CSV 导入。关键点逐行校验 事务回滚 错误明细反馈不能“成功 499 条失败 1 条就全盘放弃”def import_students_from_csv(self, csv_path: str) - Dict[str, List[str]]: 从 CSV 导入学生返回成功/失败列表 import csv success_list [] fail_list [] try: with open(csv_path, r, encodingutf-8-sig) as f: # utf-8-sig 解决 Excel 保存的 CSV BOM 问题 reader csv.DictReader(f) # 必须包含这些列 required_cols {student_id, name, gender, class_code} if not required_cols.issubset(set(reader.fieldnames)): raise ValueError(fCSV 缺少必要列{required_cols}) # 开启事务 self.conn.execute(BEGIN TRANSACTION) for i, row in enumerate(reader, 1): try: # 字段清洗 student_id row[student_id].strip() name row[name].strip() gender row[gender].strip() class_code row[class_code].strip() if not all([student_id, name, gender, class_code]): raise ValueError(f第{i}行数据不完整) if gender not in [男, 女, 其他]: raise ValueError(f第{i}行性别非法{gender}) if self.add_student(student_id, name, gender, class_code): success_list.append(f{student_id}({name})) else: fail_list.append(f第{i}行{student_id} 添加失败) except Exception as e: fail_list.append(f第{i}行{e}) self.conn.commit() print(f✅ 导入完成成功 {len(success_list)} 条失败 {len(fail_list)} 条) except Exception as e: self.conn.rollback() print(f❌ 导入中断已回滚{e}) fail_list.append(f全局错误{e}) return {success: success_list, fail: fail_list} 实测要点encodingutf-8-sig是血泪经验——Windows Excel 默认保存 CSV 带 BOM不用此参数student_id会读成\ufeff2023001导致UNIQUE冲突self.conn.execute(BEGIN TRANSACTION)显式开启事务确保 500 行中任意一行失败前面 499 行不生效返回字典含明细列表班主任可直接复制失败行号去核对原始表。4. 报表与导出让班主任能直接打印的 Excel 成绩单不是程序员眼里的“数据导出”4.1 生成班级成绩单用 pandas 整合 SQL 查询结果班主任最常要的是“高二3班全体学生成绩单”含姓名、学号、各科成绩、总分、班级排名。pandas是最优解——它能把 SQL 查询结果自动转 DataFrame并支持复杂计算# report.py import pandas as pd import sqlite3 def generate_class_report(class_code: str, db_path: str school.db) - pd.DataFrame: 生成指定班级成绩单 DataFrame conn sqlite3.connect(db_path) # 一步查出班级所有学生及其各科成绩用 pivot 实现科目转列 query SELECT s.student_id, s.name, s.gender, c.course_name, sc.score FROM students s JOIN classes cl ON s.class_id cl.id JOIN scores sc ON s.student_id sc.student_id JOIN courses c ON sc.course_id c.id WHERE cl.class_code ? ORDER BY s.student_id, c.course_name df_raw pd.read_sql_query(query, conn, params(class_code,)) conn.close() if df_raw.empty: print(f⚠️ 班级 {class_code} 无学生或无成绩数据) return pd.DataFrame() # pivot把 course_name 变成列score 填入 # aggfuncfirst 处理同一学生同一课程多成绩取第一条如需取最高分用 max df_pivot df_raw.pivot_table( index[student_id, name, gender], columnscourse_name, valuesscore, aggfuncfirst ).reset_index() # 计算总分、平均分、排名 score_cols [col for col in df_pivot.columns if col not in [student_id, name, gender]] df_pivot[总分] df_pivot[score_cols].sum(axis1, skipnaTrue) df_pivot[平均分] df_pivot[score_cols].mean(axis1, skipnaTrue).round(2) df_pivot[班级排名] df_pivot[总分].rank(methodmin, ascendingFalse).astype(int) # 列顺序整理 final_cols [班级排名, student_id, name, gender] score_cols [总分, 平均分] return df_pivot[final_cols].sort_values(班级排名) # 示例调用 if __name__ __main__: df generate_class_report(G2-03) print(df.head())输出效果DataFrame班级排名 student_id name gender 数学 英语 物理 总分 平均分 0 1 2023001 张三 男 92 85 88 265 88.33 1 2 2023002 李四 男 89 90 85 264 88.00 ...✅ 优势pivot_table自动处理“一学生多科目”转“一行多列”比手写for循环拼字典干净十倍rank(methodmin)实现并列排名如两人总分同为 265都排第 1下一位排第 3skipnaTrue确保某科缺考NaN不影响总分计算sum忽略NaN。4.2 导出带格式的 Excel用 openpyxl 控制列宽、冻结首行、设置标题样式pandas.to_excel()只能导出纯数据班主任要的是“打开就能打印”的成品。openpyxl可精细控制# export.py from openpyxl import Workbook from openpyxl.styles import Font, Alignment, PatternFill, Border, Side from openpyxl.utils.dataframe import dataframe_to_rows import pandas as pd def export_class_report_to_excel(df: pd.DataFrame, class_code: str, filename: str): 导出带格式的成绩单 Excel wb Workbook() ws wb.active ws.title f{class_code}成绩单 # 写入数据dataframe_to_rows 保留索引 for r_idx, row in enumerate(dataframe_to_rows(df, indexFalse, headerTrue), 1): for c_idx, value in enumerate(row, 1): cell ws.cell(rowr_idx, columnc_idx, valuevalue) # 标题行加粗、居中、浅灰底 if r_idx 1: cell.font Font(boldTrue, size11) cell.alignment Alignment(horizontalcenter, verticalcenter) cell.fill PatternFill(start_colorD3D3D3, end_colorD3D3D3, fill_typesolid) else: cell.alignment Alignment(horizontalcenter, verticalcenter) # 设置列宽按中文字符数估算 for col in ws.columns: max_length 0 column col[0].column_letter for cell in col: try: if len(str(cell.value)) max_length: max_length len(str(cell.value)) except: pass adjusted_width min(max_length 2, 20) # 最大20字符宽 ws.column_dimensions[column].width adjusted_width # 冻结首行 ws.freeze_panes A2 # 添加页眉页脚 ws.oddHeader.center.text fB{class_code} 学生成绩单b{pd.Timestamp.now().strftime(%Y年%m月%d日)} ws.oddFooter.center.text A P/N wb.save(filename) print(f✅ 成绩单已导出{filename}) # 使用示例 df generate_class_report(G2-03) export_class_report_to_excel(df, G2-03, G2-03_成绩单_20241025.xlsx) 关键细节ws.freeze_panes A2让滚动时首行始终可见班主任查名单不迷路oddHeader.center.text插入动态日期避免手写时间出错列宽自动适配内容长度但上限 20防止“数学”列宽 50 挤垮整个表PatternFill给标题行加灰底视觉区分一目了然。4.3 生成学期统计报表用 SQL 直接聚合不依赖 pandas教务处要的不是单班名单而是全校“2024-2025学年第一学期各科及格率”。这种聚合计算直接用 SQL 比 pandas 更快、更省内存def get_semester_stats(semester: str, db_path: str school.db) - pd.DataFrame: 查询指定学期各科及格率≥60分为及格 conn sqlite3.connect(db_path) query SELECT c.course_name, COUNT(*) as total_count, COUNT(CASE WHEN sc.score 60 THEN 1 END) as pass_count, ROUND(COUNT(CASE WHEN sc.score 60 THEN 1 END) * 100.0 / COUNT(*), 2) as pass_rate FROM scores sc JOIN courses c ON sc.course_id c.id WHERE c.semester ? GROUP BY c.course_name ORDER BY pass_rate DESC df pd.read_sql_query(query, conn, params(semester,)) conn.close() return df # 调用 stats_df get_semester_stats(2024-2025学年第一学期) print(stats_df) # 输出 # course_name total_count pass_count pass_rate # 0 数学 120 108 90.00 # 1 英语 120 102 85.00 # 2 物理 120 96 80.00 为什么不用 pandas 做df[df[score]60].groupby(course_name).size()SQL 在数据库引擎内完成聚合只需传回 3 行结果pandas 需把全部成绩记录可能上万行加载到内存再计算内存占用翻倍速度慢 5 倍以上ROUND(..., 2)直接返回两位小数避免 Python 浮点精度问题。5. 避坑指南那些让你加班到凌晨的“小问题”其实都有标准解法5.1 现象导出 Excel 时中文全是方框□□□原因openpyxl默认字体不支持中文Windows 上用CalibriLinux/macOS 用Arial均无中文字体。解决强制设置中文字体。在export_class_report_to_excel函数中添加字体设置from openpyxl.styles import Font # 在写入数据循环内替换原 cell.font 设置 if r_idx 1: cell.font Font(name微软雅黑, boldTrue, size11) # Windows 推荐 # macOS 可用 Font(namePingFang SC, boldTrue, size11) # Linux 可用 Font(nameWenQuanYi Zen Hei, boldTrue, size11) else: cell.font Font(name微软雅黑, size10)提示若目标环境不确定可先检查系统字体fc-list :langzhLinux、system_profiler SPFontsDataType | grep -A 10 ChinesemacOS。5.2 现象add_student时提示 “UNIQUE constraint failed: students.student_id”但学号明明没重复原因学号字符串含不可见字符如 Excel 复制时带的\xa0不间断空格或 Word 粘贴的全角空格。解决在add_student方法开头清洗字符串student_id student_id.strip().replace(\u00a0, ).replace( , ).replace( , ) # \u00a0 是 NO-BREAK SPACE‘ ’是全角空格5.3 现象get_student_scores查不到成绩但数据库里明明有原因students.student_id是TEXT类型但scores.student_id也是TEXT若插入时用了str(123)而不是123SQLite 会隐式转换但JOIN时类型不一致导致匹配失败。解决严格保证所有student_id以字符串形式传入。在add_score中增加类型检查if not isinstance(student_id, str): raise TypeError(fstudent_id 必须是字符串当前类型{type(student_id).__name__})5.4 现象批量导入 CSV 后部分学生性别显示为None原因CSV 中该列为空row[gender]返回空字符串但 not in [男,女,其他]为True触发ValueError整行跳过。解决允许空性别存入数据库时设为NULLgender row[gender].strip() if gender in [男, 女, 其他]: pass elif not gender: # 空字符串 gender None else: raise ValueError(f性别非法{gender})然后修改建表语句gender TEXT CHECK(gender IN (男, 女, 其他) OR gender IS NULL)。5.5 现象generate_class_report生成的排名总分相同者排名不连续如 1,2,2,4原因pandas.rank(methodmin)是正确用法但若总分列含NaN某生缺考rank()默认na_optionkeepNaN会被排在最后如第 50 名导致非NaN行排名错位。解决计算排名前先填充NaN为 0或 -1再排名df_pivot[总分] df_pivot[score_cols].sum(axis1, skipnaTrue) # 填充 NaN 为 0避免影响排名 df_pivot[总分] df_pivot[总分].fillna(0) df_pivot[班级排名] df_pivot[总分].rank(methodmin, ascendingFalse).astype(int)6. 进阶技巧用命令行参数驱动让系统真正脱离 IDE成为班主任桌面上的“双击即用”工具6.1 构建主入口main.py支持子命令像 Git 一样使用班主任不会开 VS Code 写 Python她需要一个.exe或双击运行的脚本。第一步用argparse实现命令行接口让系统可被集成到批处理中# main.py import argparse from manager import StudentManager from report import generate_class_report from export import export_class_report_to_excel def main(): parser argparse.ArgumentParser(description学生成绩管理系统 CLI 工具) subparsers parser.add_subparsers(destcommand, help可用命令) # 初始化数据库 parser_init subparsers.add_parser(init, help初始化数据库) # 添加学生 parser_add_stu subparsers.add_parser(add-student, help添加学生) parser_add_stu.add_argument(--id, requiredTrue, help学号) parser_add_stu.add_argument(--name, requiredTrue, help姓名) parser_add_stu.add_argument(--gender, choices[男, 女, 其他], requiredTrue, help性别) parser_add_stu.add_argument(--class, destclass_code, requiredTrue, help班级编码) # 添加成绩 parser_add_score subparsers.add_parser(add-score, help添加成绩) parser_add_score.add_argument(--student-id, requiredTrue, help学生学号) parser_add_score.add_argument(--course-code, requiredTrue, help课程编码) parser_add_score.add_argument(--score, typefloat, requiredTrue, help分数) parser_add_score.add_argument(--exam-type, default期中, help考试类型期中/期末/随堂/补考) # 生成并导出成绩单 parser_export subparsers.add_parser(export-report, help导出班级成绩单) parser_export.add_argument(--class, destclass_code, requiredTrue, help班级编码) parser_export.add_argument(--output, requiredTrue, help输出 Excel 文件名如 report.xlsx) args parser.parse_args() if args.command init: from manager import init_database init_database() elif args.command add-student: with StudentManager() as mgr: mgr.add_student(args.id, args.name, args.gender, args.class_code) elif args.command add-score: with StudentManager() as mgr: mgr.add_score(args.student_id, args.course_code, args.score, args.exam_type) elif args.command export-report: df generate_class_report(args.class_code) if not df.empty: export_class_report_to_excel(df, args.class_code, args.output) else: print(❌ 无数据可导出) if __name__ __main__: main()现在班主任可以这样用# 初始化首次运行 python main.py init # 添加学生 python main.py add-student --id 2023001 --name 张三 --gender 男 --class G2-03 # 添加成绩 python main.py add-score --student-id 2023001 --course-code MATH-2024Q3 --score 92 --exam-type 期中 # 导出成绩单生成 G2-03_Report.xlsx python main.py export-report --class G2-03 --output G2-03_Report.xlsx6.2 打包成独立可执行文件PyInstaller 一键生成score_manager.exe让班主任双击运行无需安装 Pythonpip install pyinstaller pyinstaller --onefile --console --name score_manager main.py生成的dist/score_manager.exe可直接发给老师。✅--onefile打包成单个 exe✅本文还有配套的精品资源点击获取