成绩小管家 Pro 插画

还记得第十一课的小管家吗?它已经能存数据(十二课)、能画图(十四课)了。这一课是 Python 线的总装:把所有本事合到一个程序里,再补上查询、删除、统计报告三个新功能,做成一个真正能用的"成绩小管家 Pro"。学完这一课,你就有了一个可以投到作品区的完整作品。

一、Pro 版多了什么

菜单功能新旧
1录入成绩(存完自动保存到文件)十一 + 十二课
2查看全部十一课
3按姓名查成绩★新
4删除学生★新
5统计报告(平均分/最高分/及格率)★新(十一课的升级)
6生成成绩图(存成图片)十四课
0退出—

二、新功能逐个看

查成绩:在列表里一个一个比对姓名,找到了就打印、收工(return);全比对完还没有,就是没这个人。这个"从头找到尾"的办法有个正经名字——顺序查找,先记住它,学考那边还会碰面。

def find_student():
    name = input("要查谁的成绩:")
    for s in students:
        if s["姓名"] == name:
            print(f"{name} 的成绩是 {s['成绩']} 分")
            return            # 找到了,收工
    print("没找到这个学生")     # 循环走完都没 return,说明没有

删除学生:和查找是同一个套路,只是找到后多一步 remove 把他从列表拿掉,并且立刻 save_data() 同步到文件——不然下次启动,他又"复活"了(第十二课挑战题的坑,就是这个)。

def delete_student():
    name = input("要删除谁:")
    for s in students:
        if s["姓名"] == name:
            students.remove(s)
            save_data()       # 别忘了同步文件!
            print(f"已删除 {name}")
            return
    print("没找到这个学生")

统计报告:把平均分、最高分、及格率一次算清。及格率就是"及格人数 ÷ 总人数",循环数一遍就行:

def show_report():
    if not students:
        print("还没有数据,先录入吧")
        return
    scores = [s["成绩"] for s in students]
    avg = sum(scores) / len(scores)
    best = max(students, key=lambda s: s["成绩"])
    passed = 0
    for sc in scores:
        if sc >= 60:
            passed += 1
    rate = passed / len(scores) * 100
    print(f"共 {len(scores)} 人 | 平均分 {avg:.1f} | "
          f"最高分 {best['姓名']} {best['成绩']} 分 | 及格率 {rate:.0f}%")

其中第一行 scores = [s["成绩"] for s in students] 是个新写法,叫列表推导式:一行把所有人的成绩挑出来组成新列表。等价于写个 for 循环 append,只是更简洁,以后会常遇到。

生成成绩图:把第十四课的画图代码打包成函数,注意最后加一句 plt.close() 把图关掉——不然连续生成多次,图会叠在一起:

def make_chart():
    if not students:
        print("还没有数据,先录入吧")
        return
    names = [s["姓名"] for s in students]
    scores = [s["成绩"] for s in students]
    avg = sum(scores) / len(scores)
    plt.bar(names, scores)
    plt.axhline(avg, color="red", linestyle="--")    # 平均线
    plt.axhline(60, color="green", linestyle=":")     # 及格线
    plt.title(f"全班成绩(平均分 {avg:.1f})")
    plt.xlabel("姓名")
    plt.ylabel("成绩")
    plt.ylim(0, 100)
    plt.savefig("score_chart.png")
    plt.close()
    print("成绩图已保存为 score_chart.png")

三、完整代码(可直接抄走运行)

import os
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

students = []
FILENAME = "scores.txt"

def save_data():
    with open(FILENAME, "w", encoding="utf-8") as f:
        for s in students:
            f.write(f"{s['姓名']},{s['成绩']}\n")

def load_data():
    if not os.path.exists(FILENAME):
        return
    with open(FILENAME, "r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            name, score = line.split(",")
            students.append({"姓名": name, "成绩": float(score)})

def add_student():
    name = input("姓名:")
    score = float(input("成绩:"))
    students.append({"姓名": name, "成绩": score})
    save_data()
    print("录入成功!")

def show_all():
    if not students:
        print("还没有数据,先录入吧")
        return
    for s in students:
        print(f"{s['姓名']}:{s['成绩']}分")

def find_student():
    name = input("要查谁的成绩:")
    for s in students:
        if s["姓名"] == name:
            print(f"{name} 的成绩是 {s['成绩']} 分")
            return
    print("没找到这个学生")

def delete_student():
    name = input("要删除谁:")
    for s in students:
        if s["姓名"] == name:
            students.remove(s)
            save_data()
            print(f"已删除 {name}")
            return
    print("没找到这个学生")

def show_report():
    if not students:
        print("还没有数据,先录入吧")
        return
    scores = [s["成绩"] for s in students]
    avg = sum(scores) / len(scores)
    best = max(students, key=lambda s: s["成绩"])
    passed = 0
    for sc in scores:
        if sc >= 60:
            passed += 1
    rate = passed / len(scores) * 100
    print(f"共 {len(scores)} 人 | 平均分 {avg:.1f} | "
          f"最高分 {best['姓名']} {best['成绩']} 分 | 及格率 {rate:.0f}%")

def make_chart():
    if not students:
        print("还没有数据,先录入吧")
        return
    names = [s["姓名"] for s in students]
    scores = [s["成绩"] for s in students]
    avg = sum(scores) / len(scores)
    plt.bar(names, scores)
    plt.axhline(avg, color="red", linestyle="--")
    plt.axhline(60, color="green", linestyle=":")
    plt.title(f"全班成绩(平均分 {avg:.1f})")
    plt.xlabel("姓名")
    plt.ylabel("成绩")
    plt.ylim(0, 100)
    plt.savefig("score_chart.png")
    plt.close()
    print("成绩图已保存为 score_chart.png")

load_data()
while True:
    print("1.录入 2.查看全部 3.查成绩 4.删学生 5.统计报告 6.生成成绩图 0.退出")
    choice = input("请选择:")
    if choice == "1":
        add_student()
    elif choice == "2":
        show_all()
    elif choice == "3":
        find_student()
    elif choice == "4":
        delete_student()
    elif choice == "5":
        show_report()
    elif choice == "6":
        make_chart()
    elif choice == "0":
        print("再见!数据都存在 scores.txt 里了。")
        break
    else:
        print("没有这个选项,重新选吧")

四、回头看:你已经会了多少

这个程序的部位来自哪一课
菜单 + while 循环 + if 分支第六、七课
字典装进列表的数据结构第八、九课
每个功能一个函数第十课
save / load 文件存取第十二课
split 拆行、字符串拼接第十三课
画图存图第十四课

不知不觉,你已经能独立写出一个上百行、功能完整的小软件了。这就是编程学习的正常节奏:每课一小块,最后拼成一个大件。把你的小管家 Pro 打磨一下(换个主题也行:通讯录、图书管理、零花钱账本,骨架都一样),投到本站的作品区去吧。

练一练

1. 排行榜:给菜单加"7.排行榜":把学生按成绩从高到低打印出来(提示:sorted(students, key=lambda s: s["成绩"], reverse=True))。
2. 改成绩:加"8.改成绩":先按姓名找到人,再改他的成绩,别忘保存。想一想它跟哪两个功能是同套路?
3. 挑战题:如果每人要存三科成绩(语数英),数据结构该怎么改?文件一行该怎么写?先在纸上设计,别急着写代码——真实开发就是先设计、后动手。

下一课是 Python 线的彩蛋:用 turtle 海龟绘图,纯玩,放松一下。

More

相关教程