Aller au contenu principal

Lab 03 — Student Grade Book

Hands-on Lab 45 min Beginner

Objectives

  1. Use dicts to model student records
  2. Use lists to store collections of records
  3. Use sets to track unique categories
  4. Use Counter to generate statistics
  5. Use defaultdict to group students by grade

Step 1 — Create the Grade Book

mkdir ~/python-course/lab-03 && cd ~/python-course/lab-03

Create gradebook.py:

#!/usr/bin/env python3
"""
Lab 03 — Student Grade Book
Demonstrates lists, dicts, sets, and collections module.
"""

from collections import Counter, defaultdict


GRADE_SCALE = [
(90, "A", "Excellent"),
(80, "B", "Good"),
(70, "C", "Satisfactory"),
(60, "D", "Passing"),
(0, "F", "Failing"),
]


def letter_grade(score):
"""Convert numeric score to letter grade."""
for threshold, letter, _ in GRADE_SCALE:
if score >= threshold:
return letter
return "F"


def grade_label(score):
"""Return descriptive label for a score."""
for threshold, _, label in GRADE_SCALE:
if score >= threshold:
return label
return "Failing"


def add_student(gradebook, name, scores):
"""Add a student with a list of scores."""
avg = sum(scores) / len(scores)
gradebook[name] = {
"scores": scores,
"average": round(avg, 2),
"grade": letter_grade(avg),
"label": grade_label(avg),
"highest": max(scores),
"lowest": min(scores),
}


def print_report(gradebook):
"""Print a formatted grade report."""
print("\n" + "=" * 60)
print(f"{'STUDENT GRADE REPORT':^60}")
print("=" * 60)
print(f" {'Name':<18} {'Avg':>6} {'Grade':>6} Status")
print("-" * 60)

for name, data in sorted(gradebook.items(), key=lambda x: x[1]["average"], reverse=True):
print(f" {name:<18} {data['average']:>6.1f} {data['grade']:>6} {data['label']}")

print("=" * 60)


def class_statistics(gradebook):
"""Compute and display class-wide statistics."""
averages = [d["average"] for d in gradebook.values()]
grade_counts = Counter(d["grade"] for d in gradebook.values())

print("\n📊 Class Statistics")
print(f" Students : {len(gradebook)}")
print(f" Class avg : {sum(averages)/len(averages):.1f}")
print(f" Highest avg : {max(averages):.1f}")
print(f" Lowest avg : {min(averages):.1f}")
print(f"\n Grade Distribution:")
for grade in ["A", "B", "C", "D", "F"]:
count = grade_counts.get(grade, 0)
bar = "█" * count
print(f" {grade}: {bar} ({count})")


def group_by_grade(gradebook):
"""Group student names by their letter grade."""
groups = defaultdict(list)
for name, data in gradebook.items():
groups[data["grade"]].append(name)

print("\n📋 Students by Grade:")
for grade in sorted(groups):
print(f" {grade}: {', '.join(sorted(groups[grade]))}")


def find_top_students(gradebook, n=3):
"""Return top N students by average."""
ranked = sorted(gradebook.items(), key=lambda x: x[1]["average"], reverse=True)
print(f"\n🏆 Top {n} Students:")
for i, (name, data) in enumerate(ranked[:n], 1):
print(f" #{i}: {name}{data['average']:.1f} ({data['grade']})")


def subjects_passed(gradebook, passing=60):
"""Show which students have no failing scores."""
print(f"\n✅ Students with all scores ≥ {passing}:")
for name, data in gradebook.items():
all_passed = all(s >= passing for s in data["scores"])
if all_passed:
print(f" {name}")


def main():
gradebook = {}

students_data = [
("Alice Martin", [92, 88, 95, 91, 87]),
("Bob Nguyen", [78, 82, 75, 80, 79]),
("Charlie Smith", [65, 70, 68, 72, 66]),
("Diana Tremblay", [55, 48, 60, 52, 58]),
("Eve Wilson", [98, 97, 99, 96, 98]),
("Frank Dubois", [83, 79, 85, 88, 81]),
("Grace Kim", [72, 68, 74, 70, 73]),
("Henry Lavoie", [45, 50, 42, 55, 48]),
("Isabelle Roy", [91, 93, 89, 95, 92]),
("Jack Thompson", [76, 80, 77, 74, 78]),
]

for name, scores in students_data:
add_student(gradebook, name, scores)

print_report(gradebook)
class_statistics(gradebook)
group_by_grade(gradebook)
find_top_students(gradebook, n=3)
subjects_passed(gradebook)

# Set demo: unique grades achieved
grades_given = {data["grade"] for data in gradebook.values()}
print(f"\n🎓 Grades awarded: {sorted(grades_given)}")

# Tuple demo: top student as immutable record
top = max(gradebook.items(), key=lambda x: x[1]["average"])
record = (top[0], top[1]["average"], top[1]["grade"])
print(f"\n👑 Top record (immutable): {record}")


if __name__ == "__main__":
main()

Step 2 — Run

python3 gradebook.py

Verification ✅

============================================================
STUDENT GRADE REPORT
============================================================
Name Avg Grade Status
------------------------------------------------------------
Eve Wilson 97.6 A Excellent
Isabelle Roy 92.0 A Excellent
Alice Martin 90.6 A Excellent
...
============================================================

📊 Class Statistics
Students : 10
Class avg : 74.4
...

🏆 Top 3 Students:
#1: Eve Wilson — 97.6 (A)
#2: Isabelle Roy — 92.0 (A)
#3: Alice Martin — 90.6 (A)

Bonus

Add an interactive_mode() that lets the user:

  1. Add a new student via input()
  2. Look up any student by name
  3. Remove a student with del

Summary

You used:

  • dict to model complex student records with nested data
  • list to store multiple score values
  • set comprehension to find unique grades awarded
  • tuple for an immutable top-student record
  • Counter to count grade distribution
  • defaultdict(list) to group students by letter grade
  • sorted() with a key=lambda to rank students