Project: a grade book

Intermediate

Introduction

This project puts several lessons together: a dictionary of students, a function for grades, a loop over the data, and formatted output. Real programs are mostly this: small, familiar pieces combined.

You now know enough to build something complete. The hard part of a real program is rarely any single line: it is deciding how to organise the pieces so that the whole thing stays simple.

How it works

The plan

  1. Store the data: a dictionary from each name to a list of marks.
  2. Write one small function for one job: turning an average into a grade.
  3. Loop over the students, compute each average, and print a neat report line.

The top student

book = {"Rahim": [72, 68, 80], "Nadia": [91, 88, 95], "Karim": [55, 61, 70]}
averages = {name: sum(m) / len(m) for name, m in book.items()}
top = max(averages, key=averages.get)
print("Top student:", top)
Open in compiler

A dictionary comprehension builds name → average, and max(averages, key=averages.get) finds the key with the largest value.

Make it yours

These extensions each use one earlier lesson:

  • Read the students from input lines like Rahim 72 68 80 (text processing).
  • Reject marks outside 0 to 100 with a ValueError (raising errors).
  • Add asserts for grade at the boundaries 80, 70 and 60 (testing).
  • Turn it into a GradeBook class with add_student and report methods (classes).

Syntax

book = {"Rahim": [72, 68, 80]}
for name, marks in book.items():
    avg = sum(marks) / len(marks)
    print(f"{name:<6}{avg:6.1f}")

Example

def grade(avg):
    if avg >= 80:
        return "A+"
    if avg >= 70:
        return "A"
    if avg >= 60:
        return "B"
    return "C"

book = {"Rahim": [72, 68, 80], "Nadia": [91, 88, 95], "Karim": [55, 61, 70]}
for name, marks in book.items():
    avg = sum(marks) / len(marks)
    print(f"{name:<6}{avg:6.1f}  {grade(avg)}")
Open in compiler

Step through it. Each round of the loop takes one name and its list of marks; grade(avg) runs in its own frame and returns a letter. The f-string format codes from the formatting lesson line up the columns.

Line by line

  1. grade turns an average into a letter, and it does nothing else.
  2. book maps each name to a list of marks.
  3. The loop gets one name and its marks on each pass.
  4. avg is worked out from the marks.
  5. The f-string lines up the columns, and calls grade for the last one.

Expected result

Rahim   73.3  A
Nadia   91.3  A+
Karim   62.0  B

In the real world

School systems, payroll software and sales reports are all larger versions of this program: read records, calculate, and present. Keeping the calculation separate from the display is one of the most useful design habits.

Common mistakes

  • Dividing by len(marks) when a student has no marks yet, which raises ZeroDivisionError.
  • Putting the grade boundaries in the wrong order, so everyone gets the first grade that matches.
  • Printing inside grade: let the function return the letter and let the caller decide how to show it.

Best practices

  • Decide what shape your data has before writing any code.
  • Keep calculation and printing in separate functions.
  • Run the program after each small addition.

Summary

A complete program combines a data structure, small functions, a loop and formatted output. Choosing the shape of the data and giving each function one job keep it simple.

Practice

Extend the grade book to print the class average and how many students got each grade.

Quick check

1. Why is grade a separate function?
2. What does max(averages, key=averages.get) return?
3. What does {avg:6.1f} do?

Key takeaways

  • Real programs are built from small, familiar pieces.
  • Decide on the shape of the data first.
  • Add one feature at a time and test it.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.