Python: 综合项目:学生成绩管理(下)

前两课完成了数据层和逻辑层。这一课把一切串起来——写一个完整的命令行菜单界面。完成这课后,你就拥有了一个真正可用的学生成绩管理系统。


1. 完整系统代码

将前三课的代码合并到一个文件中,加上菜单界面:

▶ 示例:完整成绩管理系统

TEXT 📖 仅展示
import json
import os
import statistics

# ====== 配置 ======
DATA_FILE = "grade_system.json"

DEFAULT_DATA = {
    "students": {},
    "subjects": ["语文", "数学", "英语"],
    "classes": []
}

# ====== 数据层 ======

def load_data():
    if not os.path.exists(DATA_FILE):
        save_data(DEFAULT_DATA.copy())
        return DEFAULT_DATA.copy()
    with open(DATA_FILE, "r", encoding="utf-8") as f:
        return json.load(f)

def save_data(data):
    with open(DATA_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def add_student(student_id, name, class_name):
    data = load_data()
    if student_id in data["students"]:
        return False, f"学号 {student_id} 已存在!"
    data["students"][student_id] = {"name": name, "class_name": class_name, "scores": {}}
    if class_name not in data["classes"]:
        data["classes"].append(class_name)
    save_data(data)
    return True, f"已添加学生:{name}({student_id})"

def delete_student(student_id):
    data = load_data()
    if student_id not in data["students"]:
        return False, f"学号 {student_id} 不存在!"
    name = data["students"][student_id]["name"]
    del data["students"][student_id]
    save_data(data)
    return True, f"已删除学生:{name}"

def add_score(student_id, subject, score):
    data = load_data()
    if student_id not in data["students"]:
        return False, f"学号 {student_id} 不存在!"
    if subject not in data["subjects"]:
        return False, f"科目 {subject} 不存在!"
    if not isinstance(score, (int, float)) or score < 0 or score > 100:
        return False, "成绩必须在 0~100 之间!"
    data["students"][student_id]["scores"][subject] = score
    save_data(data)
    return True, f"已录入 {data['students'][student_id]['name']} 的 {subject}:{score}分"

# ====== 逻辑层 ======

def get_student_report(student_id):
    data = load_data()
    student = data["students"].get(student_id)
    if not student:
        return None
    report = {"name": student["name"], "class_name": student["class_name"],
              "scores": dict(student["scores"])}
    scores = list(student["scores"].values())
    if scores:
        report["average"] = round(sum(scores) / len(scores), 1)
        report["total"] = sum(scores)
        report["max_score"] = max(scores)
        report["min_score"] = min(scores)
    else:
        report["average"] = report["total"] = report["max_score"] = report["min_score"] = None
    return report

def get_class_ranking(class_name=None):
    data = load_data()
    results = []
    for sid, info in data["students"].items():
        if class_name and info["class_name"] != class_name:
            continue
        scores = list(info["scores"].values())
        total = sum(scores) if scores else 0
        avg = round(total / len(scores), 1) if scores else 0
        results.append({"id": sid, "name": info["name"], "class": info["class_name"],
                        "total": total, "avg": avg, "count": len(scores)})
    results.sort(key=lambda x: x["total"], reverse=True)
    for i, r in enumerate(results, 1):
        r["rank"] = i
    return results

def get_subject_stats():
    data = load_data()
    stats = {}
    for subject in data["subjects"]:
        scores = []
        for info in data["students"].values():
            if subject in info["scores"]:
                scores.append(info["scores"][subject])
        if scores:
            stats[subject] = {"avg": round(sum(scores) / len(scores), 1),
                              "max": max(scores), "min": min(scores), "count": len(scores)}
    return stats

# ====== 展示层 ======

def show_main_menu():
    print("\n" + "=" * 40)
    print("🎓 学生成绩管理系统")
    print("=" * 40)
    print("1. 学生管理")
    print("2. 成绩管理")
    print("3. 统计查询")
    print("4. 查看数据")
    print("5. 退出")
    print("=" * 40)

def show_student_menu():
    print("\n--- 学生管理 ---")
    print("1. 添加学生")
    print("2. 删除学生")
    print("3. 返回主菜单")

def show_score_menu():
    print("\n--- 成绩管理 ---")
    print("1. 录入成绩")
    print("2. 返回主菜单")

def show_stats_menu():
    print("\n--- 统计查询 ---")
    print("1. 个人成绩单")
    print("2. 全校排名")
    print("3. 各科平均分")
    print("4. 返回主菜单")

def student_management():
    """学生管理子菜单"""
    while True:
        show_student_menu()
        choice = input("请选择:").strip()
        
        if choice == "1":
            sid = input("学号:").strip()
            name = input("姓名:").strip()
            cls = input("班级:").strip()
            success, msg = add_student(sid, name, cls)
            print(f"  {'✅' if success else '❌'} {msg}")
        
        elif choice == "2":
            sid = input("请输入要删除的学号:").strip()
            success, msg = delete_student(sid)
            print(f"  {'✅' if success else '❌'} {msg}")
        
        elif choice == "3":
            break
        
        else:
            print("无效选择!")

def score_management():
    """成绩管理子菜单"""
    while True:
        show_score_menu()
        choice = input("请选择:").strip()
        
        if choice == "1":
            sid = input("学号:").strip()
            subject = input("科目:").strip()
            try:
                score = float(input("成绩(0-100):").strip())
            except ValueError:
                print("❌ 成绩必须是数字!")
                continue
            success, msg = add_score(sid, subject, score)
            print(f"  {'✅' if success else '❌'} {msg}")
        
        elif choice == "2":
            break
        
        else:
            print("无效选择!")

def stats_query():
    """统计查询子菜单"""
    while True:
        show_stats_menu()
        choice = input("请选择:").strip()
        
        if choice == "1":
            sid = input("请输入学号:").strip()
            report = get_student_report(sid)
            if not report:
                print("❌ 未找到该学生!")
                continue
            print(f"\n📋 {report['name']}({report['class_name']})成绩单")
            print("-" * 25)
            for subject, score in report["scores"].items():
                print(f"  {subject}:{score}")
            if report["total"] is not None:
                print(f"  总分:{report['total']}")
                print(f"  平均分:{report['average']}")
                print(f"  最高分:{report['max_score']}")
                print(f"  最低分:{report['min_score']}")
            else:
                print("  (暂无成绩)")
        
        elif choice == "2":
            ranking = get_class_ranking()
            if not ranking:
                print("暂无数据!")
                continue
            print("\n🏆 全校排名")
            print(f"{'排名':<4}{'姓名':<8}{'班级':<8}{'总分':<6}{'平均分':<6}")
            print("-" * 32)
            for r in ranking:
                print(f"{r['rank']:<4}{r['name']:<8}{r['class']:<8}{r['total']:<6}{r['avg']:<6}")
        
        elif choice == "3":
            stats = get_subject_stats()
            if not stats:
                print("暂无成绩数据!")
                continue
            print("\n📊 各科成绩统计")
            print(f"{'科目':<8}{'平均分':<8}{'最高':<6}{'最低':<6}{'人数':<4}")
            print("-" * 32)
            for subject, info in stats.items():
                print(f"{subject:<8}{info['avg']:<8}{info['max']:<6}{info['min']:<6}{info['count']:<4}")
        
        elif choice == "4":
            break
        
        else:
            print("无效选择!")

def view_data():
    """查看原始数据"""
    data = load_data()
    students = data["students"]
    if not students:
        print("📭 暂无学生数据")
        return
    
    print(f"\n📚 共有 {len(students)} 名学生")
    for sid, info in sorted(students.items()):
        scores = info["scores"]
        score_str = ", ".join(f"{s}:{v}" for s, v in scores.items()) if scores else "暂无成绩"
        print(f"  {sid} {info['name']}({info['class_name']})→ {score_str}")

# ====== 主程序 ======

def main():
    print("🎓 欢迎使用学生成绩管理系统!")
    
    while True:
        show_main_menu()
        choice = input("请选择操作 (1-5):").strip()
        
        if choice == "1":
            student_management()
        elif choice == "2":
            score_management()
        elif choice == "3":
            stats_query()
        elif choice == "4":
            view_data()
        elif choice == "5":
            print("感谢使用,再见!👋")
            break
        else:
            print("无效选择,请输入 1-5。")


if __name__ == "__main__":
    main()


2. 运行示例

TEXT 📖 仅展示
🎓 欢迎使用学生成绩管理系统!

========================================
🎓 学生成绩管理系统
========================================
1. 学生管理
2. 成绩管理
3. 统计查询
4. 查看数据
5. 退出
========================================
请选择操作 (1-5):1

--- 学生管理 ---
1. 添加学生
2. 删除学生
3. 返回主菜单
请选择:1
学号:2024001
姓名:Alice
班级:一班
  ✅ 已添加学生:Alice(2024001)


▶ 示例:JSON 数据读写(难度⭐)

PYTHON
import json

data = {
    "students": {
        "001": {"name": "Alice", "scores": {"数学": 90, "英语": 85}},
        "002": {"name": "Bob", "scores": {"数学": 78, "英语": 92}}
    },
    "subjects": ["数学", "英语"]
}

json_str = json.dumps(data, ensure_ascii=False, indent=2)
print("=== 序列化 ===")
print(json_str)

loaded = json.loads(json_str)
print("\n=== 反序列化 ===")
for sid, info in loaded["students"].items():
    avg = sum(info["scores"].values()) / len(info["scores"])
    print(f"{info['name']}(学号{sid})平均分:{avg:.1f}")
▶ 试一试

输出:

TEXT 📖 仅展示
=== 序列化 ===
{
  "students": {
    "001": {
      "name": "Alice",
      "scores": {
        "数学": 90,
        "英语": 85
      }
    },
    "002": {
      "name": "Bob",
      "scores": {
        "数学": 78,
        "英语": 92
      }
    }
  },
  "subjects": [
    "数学",
    "英语"
  ]
}

=== 反序列化 ===
Alice(学号001)平均分:87.5
Bob(学号002)平均分:85.0

▶ 示例:命令行菜单系统(难度⭐⭐)

PYTHON
def show_menu():
    print("\n===== 简易菜单 =====")
    print("1. 查看列表")
    print("2. 添加项目")
    print("3. 退出")

items = ["苹果", "香蕉"]

choices = ["1", "2", "3"]

for choice in choices:
    show_menu()
    print(f"用户选择:{choice}")

    if choice == "1":
        if not items:
            print("列表为空")
        else:
            for i, item in enumerate(items, 1):
                print(f"  {i}. {item}")

    elif choice == "2":
        new_item = "橙子"
        items.append(new_item)
        print(f"已添加:{new_item}")

    elif choice == "3":
        print("再见!")
        break
▶ 试一试

输出:

TEXT 📖 仅展示
===== 简易菜单 =====
1. 查看列表
2. 添加项目
3. 退出
用户选择:1
  1. 苹果
  2. 香蕉

===== 简易菜单 =====
1. 查看列表
2. 添加项目
3. 退出
用户选择:2
已添加:橙子

===== 简易菜单 =====
1. 查看列表
2. 添加项目
3. 退出
用户选择:3
再见!

3. 项目总结

类/模块 职责
数据层 load/save_data JSON 文件读写
实体层 add/delete_student 学生 CRUD
实体层 add_score 成绩 CRUD
逻辑层 get_student_report 个人成绩单
逻辑层 get_class_ranking 排名计算
逻辑层 get_subject_stats 科目统计
展示层 main/main_menu 菜单循环
展示层 student/score/stats_management 子菜单

❓ 常见问题

Q 命令行菜单的 while True 不会死循环吗?
A 不会,因为菜单里有一个选项(比如输入 0 或 q)会执行 break 跳出循环。只要确保每个菜单都有"退出"选项就行。
Q 模糊搜索用 in 就够了,还需要正则表达式吗?
A 对于这个项目 keyword in name 足够了。如果需要更复杂的搜索(比如只匹配开头、忽略大小写、多关键词组合),就需要正则表达式了,第31课会详细讲。
Q 项目完成后怎么打包给别人用?
A 最简单的方式是直接发 .py 文件,对方装了 Python 就能运行。如果想做成可执行文件(.exe),可以用 PyInstallerpip install pyinstaller && pyinstaller --onefile main.py

📖 小节


📝 作业

  1. 基础题(难度⭐):运行完整系统,添加 3 个学生和他们的成绩,生成排名报表。

  2. 进阶题(难度⭐⭐):给系统增加一个"导出报表"功能——把排名结果导出为 ranking.csv 文件(用 csv 模块)。

  3. 挑战题(难度⭐⭐⭐):不参考上面的代码,从零开始写一个"图书销售管理系统"。功能包括:添加图书(书名、作者、单价、库存)、销售登记(图书名称、数量 → 自动减少库存)、查看库存报表(按销量排序)。用 JSON 做数据持久化。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏