Class 19 - Structured data
Date: October 26, 2026
Skills: 6
Notes (review before class)
Structured Data
- Some data has several attributes that belong together: a song has a name, singer, and year; an email has a sender, subject, and body.
- Structured data: a single datum made of a fixed number of parts, called fields, which can have different types (and can themselves be structured: a date has a day, month, and year).
Defining Structured Data
A dataclass defines a new type, with a type annotation for each field
(dataclasses need from dataclasses import dataclass at the top of the file):
from dataclasses import dataclass
@dataclass
class ITunesSong:
name: str
singer: str
year: int
- The names of types we define start with a capital letter.
ITunesSongis also a constructor: a function that takes a value for each field, in order, and returns a newITunesSong.- Always follow a data definition with a few named examples:
lver = ITunesSong("La Vie en Rose", "Édith Piaf", 1945)
so = ITunesSong("Stressed Out", "twenty one pilots", 2015)
wnkkhs = ITunesSong("Waqt Ne Kiya Kya Haseen Sitam", "Geeta Dutt", 1959)
- In the directory, each example is a name for an
ITunesSongvalue, like any other. - The new type can be used in annotations:
lver: ITunesSong = ITunesSong(...). (Try annotating it asstrinstead, to see the error.)
Field Access
s.year is the year field of s:
def song_age(s: ITunesSong) -> int:
"""returns how many years old the song is"""
return 2026 - s.year
def test_song_age():
assert song_age(lver) == 81
assert song_age(so) == 11
assert song_age(wnkkhs) == 67
Recap (10 mins)
A library program keeps a record of each book: its title, author, and number of pages. Read through the program:
from dataclasses import dataclass
@dataclass
class BookRecord:
title: str
author: str
pages: int
the_dispossessed = BookRecord("The Dispossessed", "Ursula K. Le Guin", 387)
to_the_lighthouse = BookRecord("To the Lighthouse", "Virginia Woolf", 209)
brave_new_world = BookRecord("Brave New World", "Aldous Huxley", 268)
def pages_left(b: BookRecord, pages_read: int) -> int:
"""how many pages of b are left to read, after reading pages_read of them"""
return b.pages - pages_read
def test_pages_left():
assert pages_left(the_dispossessed, 0) == 387
assert pages_left(to_the_lighthouse, 9) == 200
- What is
brave_new_world.author? What ispages_left(brave_new_world, 68)? - What would
BookRecord("Brave New World", 268, "Aldous Huxley")do? Why? - If the library also recorded the year each book was published, which lines would change?
Class Exercise (45 mins)
-
Design a function that returns a "summary string" for a
BookRecord, including the title, author, and pages. -
Design a function
is_long_bookthat takes in aBookRecordand returns whether or not the book has more than 350 pages. -
Design a new data type for a
Podcast, and based on your own knowledge figure out the fields that make sense to include in it. -
Design a function
podcast_summarythat takes in aPodcastand produces a string summarizing aPodcast. -
Design a
Recipedata definition, and then write arow_to_recipefunction that consumes a row from the following table (convert numeric columns after loading):recipes_raw = load_table("https://raw.githubusercontent.com/neu-pdi/cs2000-public-resources/refs/heads/main/static/support/5-recipes.csv")
recipes_with_servings = recipes_raw.transform_column("servings", int)
recipes = recipes_with_servings.transform_column("prep time", int) -
Test your function using
recipes.row(0)or some other row number, and then use it to add a new column withadd_column.
Wrap-up (5 mins)
- Structured data groups multiple fields together.
- We define structured types using
@dataclassandclass, and create instances by calling the constructor. - Fields are accessed with the dot notation.