Python: 综合项目:学生成绩管理(中)
第 34 课完成了数据层——学生和成绩的增删改查。这一课在数据层之上构建逻辑层:统计计算、排名、分数段分析等核心业务逻辑。第 36 课再加展示层(菜单界面),就组成完整的系统。
1. 逻辑层实现
这一层的代码依赖第 34 课的数据层函数(load_data、get_all_students 等)。
▶ 示例:成绩统计逻辑层
TEXT
📖 仅展示
# ====== 逻辑层:成绩统计分析 ======
import statistics
from grade_project_part1 import load_data, get_all_students
def calculate_student_average(student_id):
"""计算单个学生的平均分"""
data = load_data()
student = data["students"].get(student_id)
if not student:
return None
scores = student["scores"].values()
if not scores:
return None
return sum(scores) / len(scores)
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["max_score"] = max(scores)
report["min_score"] = min(scores)
report["total"] = sum(scores)
else:
report["average"] = None
report["max_score"] = None
report["min_score"] = None
report["total"] = 0
return report
def get_class_ranking(class_name=None):
"""获取班级排名(按总分降序)"""
data = load_data()
students = data["students"]
# 收集所有学生(或指定班级)
results = []
for sid, info in 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({
"student_id": sid,
"name": info["name"],
"class_name": info["class_name"],
"total": total,
"average": avg,
"score_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_averages():
"""计算各科平均分"""
data = load_data()
subjects = data["subjects"]
students = data["students"]
result = {}
for subject in subjects:
scores = []
for info in students.values():
if subject in info["scores"]:
scores.append(info["scores"][subject])
if scores:
result[subject] = {
"average": round(sum(scores) / len(scores), 1),
"max": max(scores),
"min": min(scores),
"count": len(scores)
}
else:
result[subject] = None
return result
def get_score_distribution(subject=None):
"""统计分数段分布"""
data = load_data()
students = data["students"]
ranges = [
("90-100", 90, 101),
("80-89", 80, 90),
("70-79", 70, 80),
("60-69", 60, 70),
("0-59", 0, 60),
]
if subject:
# 统计某一科的分数段
result = {label: 0 for label, _, _ in ranges}
for info in students.values():
if subject in info["scores"]:
score = info["scores"][subject]
for label, low, high in ranges:
if low <= score < high:
result[label] += 1
break
return result
else:
# 统计所有科目合并的分数段
result = {label: 0 for label, _, _ in ranges}
for info in students.values():
for score in info["scores"].values():
for label, low, high in ranges:
if low <= score < high:
result[label] += 1
break
return result
def get_students_by_class():
"""按班级分组统计"""
data = load_data()
students = data["students"]
classes = {}
for sid, info in students.items():
cls = info["class_name"]
if cls not in classes:
classes[cls] = []
classes[cls].append({
"student_id": sid,
"name": info["name"],
"score_count": len(info["scores"])
})
return classes
2. 逻辑层测试
▶ 示例:逻辑层功能测试
TEXT
📖 仅展示
if __name__ == "__main__":
print("=== 个人成绩单 ===")
report = get_student_report("2024001")
if report:
print(f"姓名:{report['name']}")
print(f"班级:{report['class_name']}")
for subject, score in report["scores"].items():
print(f" {subject}:{score}")
print(f"总分:{report['total']}")
print(f"平均分:{report['average']}")
print(f"最高:{report['max_score']}")
print(f"最低:{report['min_score']}")
print("\n=== 全班排名 ===")
ranking = get_class_ranking()
for r in ranking[:5]:
print(f"第{r['rank']}名:{r['name']}({r['class_name']})总分 {r['total']}")
print("\n=== 各科平均分 ===")
averages = get_subject_averages()
for subject, info in averages.items():
if info:
print(f"{subject}:平均 {info['average']},最高 {info['max']},最低 {info['min']}")
print("\n=== 分数段分布 ===")
dist = get_score_distribution()
for label, count in dist.items():
bar = "█" * count
print(f"{label}:{bar} {count}人")
💡 注意: 上面的
import 假设数据层文件名为 grade_project_part1.py。如果你的文件名不同,修改 import 语句。最终完整系统中所有代码会合并到一个文件。
3. 异常处理设计
逻辑层中,对以下几种异常情况做了处理:
| 场景 | 返回值 | 说明 |
|---|---|---|
| 学生不存在 | None 或空结果 |
调用方检查返回值 |
| 没有成绩数据 | 统计值为 None |
调用方显示"暂无成绩" |
| 空数据 | 空列表/空字典 | 不会崩溃,友好显示 |
| 非法数据 | 异常被捕获 | 用 try-except 保护 |
▶ 示例:班级平均分计算(难度⭐)
PYTHON
scores = {
"2024001": {"name": "Alice", "class_name": "一班", "scores": {"语文": 85, "数学": 92, "英语": 88}},
"2024002": {"name": "Bob", "class_name": "一班", "scores": {"语文": 78, "数学": 85, "英语": 90}},
"2024003": {"name": "Charlie", "class_name": "二班", "scores": {"语文": 92, "数学": 88, "英语": 95}},
"2024004": {"name": "Diana", "class_name": "二班", "scores": {"语文": 70, "数学": 75, "英语": 80}},
}
def class_averages(students):
"""按班级计算各科平均分"""
classes = {}
for sid, info in students.items():
cls = info["class_name"]
if cls not in classes:
classes[cls] = {}
for subject, score in info["scores"].items():
if subject not in classes[cls]:
classes[cls][subject] = []
classes[cls][subject].append(score)
result = {}
for cls, subjects in classes.items():
result[cls] = {}
for subject, vals in subjects.items():
result[cls][subject] = round(sum(vals) / len(vals), 1)
return result
averages = class_averages(scores)
for cls, subjects in averages.items():
print(f"\n{cls}:")
for subject, avg in subjects.items():
print(f" {subject}平均分:{avg}")
输出:
TEXT
📖 仅展示
一班:
语文平均分:81.5
数学平均分:88.5
英语平均分:89.0
二班:
语文平均分:81.0
数学平均分:81.5
英语平均分:87.5
❓ 常见问题
Q 排序时
key=lambda 怎么理解?A
key 参数指定排序的依据。lambda item: item[1] 表示"取每个元素的第二项(总分)作为排序键"。等价于 def get_score(item): return item[1],lambda 只是简写。Q 统计函数返回
None 而不是 0,调用方不会麻烦吗?A 返回
None 能区分"成绩不存在"和"成绩确实是 0 分"——这是有意义的区别。调用方用 if result is not None: 判断即可,比误把"没数据"当"0分"更安全。❓ 常见问题
Q 这个概念和 XXX 有什么区别?
A 简洁对比两者的核心差异和使用场景。
📖 小节
- 逻辑层在数据层之上,专注于"计算"而不是"存取"
- 个人成绩单:展示单科成绩、总分、平均分、最高/最低分
- 班级排名:按总分降序排列,自动生成排名序号
- 各科平均分:遍历所有学生的成绩,按科目汇总统计
- 分数段分布:统计各分数区间的人数
- 异常处理:学生/成绩不存在时返回
None而非报错
📝 作业
-
基础题(难度⭐):调用
get_student_report()查看一个学生的完整成绩单,确认输出正确。 -
进阶题(难度⭐⭐):给逻辑层增加一个
get_top_n(n)函数,返回全校总分前 N 名的学生。 -
挑战题(难度⭐⭐⭐):给逻辑层增加
get_subject_pass_rate(subject, pass_score=60)函数,计算某一科的及格率(成绩 >= pass_score 的人数 / 总人数)。再计算各科及格率并输出对比。