Skip to content

Instantly share code, notes, and snippets.

@epignatelli
Created February 6, 2026 16:38
Show Gist options
  • Select an option

  • Save epignatelli/82704cb3a445df34e21c5d3e19a32f0e to your computer and use it in GitHub Desktop.

Select an option

Save epignatelli/82704cb3a445df34e21c5d3e19a32f0e to your computer and use it in GitHub Desktop.
"""Utilities for working with a tiny in-memory gradebook.
This module defines a small `Gradebook` class that stores scores for named
students and provides summary statistics such as averages.
Examples:
>>> gb = Gradebook()
>>> gb.add_score("alice", 18.0)
>>> gb.add_score("alice", 12.0)
>>> gb.average("alice")
15.0
"""
from __future__ import annotations
from typing import Dict, List
class Gradebook:
"""A minimal gradebook that stores multiple numeric scores per student.
The gradebook maps a student's name to a list of scores. Names are stored
after stripping whitespace. Scores must be finite floats.
Examples:
>>> gb = Gradebook()
>>> gb.add_score("bob", 10.0)
>>> gb.add_score("bob", 20.0)
>>> gb.scores("bob")
[10.0, 20.0]
>>> gb.average("bob")
15.0
"""
def __init__(self) -> None:
"""Initialize an empty gradebook.
Examples:
>>> gb = Gradebook()
>>> gb.scores("anyone")
[]
"""
self._scores: Dict[str, List[float]] = {}
def add_score(self, student: str, score: float) -> None:
"""Add a score for a student.
Args:
student: Student name (non-empty after stripping whitespace).
score: Numeric score to store.
Raises:
ValueError: If `student` is empty after stripping, or if `score` is
not a finite number.
Examples:
>>> gb = Gradebook()
>>> gb.add_score("alice", 17.5)
>>> gb.scores("alice")
[17.5]
"""
name = student.strip()
if not name:
raise ValueError("student must be a non-empty string")
if score != score or score in (float("inf"), float("-inf")):
raise ValueError("score must be a finite number")
self._scores.setdefault(name, []).append(float(score))
def scores(self, student: str) -> List[float]:
"""Return a copy of the scores for a student.
Args:
student: Student name.
Returns:
A new list containing that student's stored scores. If the student
has no scores, returns an empty list.
Examples:
>>> gb = Gradebook()
>>> gb.add_score("carla", 5.0)
>>> gb.scores("carla")
[5.0]
>>> gb.scores("nobody")
[]
"""
return list(self._scores.get(student.strip(), []))
def average(self, student: str) -> float:
"""Compute the average score for a student.
Args:
student: Student name.
Returns:
The arithmetic mean of the student's scores.
Raises:
ValueError: If the student has no recorded scores.
Examples:
>>> gb = Gradebook()
>>> gb.add_score("bob", 10.0)
>>> gb.add_score("bob", 20.0)
>>> gb.average("bob")
15.0
"""
name = student.strip()
values = self._scores.get(name, [])
if not values:
raise ValueError(f"no scores recorded for {name!r}")
return sum(values) / len(values)
def class_average(self) -> float:
"""Compute the average across all stored scores.
Returns:
The arithmetic mean across every score in the gradebook.
Raises:
ValueError: If there are no scores in the gradebook.
Examples:
>>> gb = Gradebook()
>>> gb.add_score("a", 10.0)
>>> gb.add_score("b", 20.0)
>>> gb.class_average()
15.0
"""
all_scores: List[float] = []
for values in self._scores.values():
all_scores.extend(values)
if not all_scores:
raise ValueError("no scores recorded in gradebook")
return sum(all_scores) / len(all_scores)
def main() -> None:
"""Run a small demonstration of the `Gradebook` class.
Examples:
$ python main.py
Alice scores: [18.0, 12.0]
Alice average: 15.0
Class average: 15.0
"""
gradebook = Gradebook()
gradebook.add_score("alice", 18.0)
gradebook.add_score("alice", 12.0)
gradebook.add_score("bob", 15.0)
print("Alice scores:", gradebook.scores("alice"))
print("Alice average:", gradebook.average("alice"))
print("Class average:", gradebook.class_average())
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment