Skip to main content

Class 19 - Structured data

Date: October 26, 2026

⚠️Homework
Homework 7 due November 1, 2026
‼️Ending Skills
Last week for skills: 10, 11
🆕New Skills
First week for skills: 5

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.
  • ITunesSong is also a constructor: a function that takes a value for each field, in order, and returns a new ITunesSong.
  • 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 ITunesSong value, like any other.
  • The new type can be used in annotations: lver: ITunesSong = ITunesSong(...). (Try annotating it as str instead, 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 is pages_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)​

  1. Design a function that returns a "summary string" for a BookRecord, including the title, author, and pages.

  2. Design a function is_long_book that takes in a BookRecord and returns whether or not the book has more than 350 pages.

  3. 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.

  4. Design a function podcast_summary that takes in a Podcast and produces a string summarizing a Podcast.

  5. Design a Recipe data definition, and then write a row_to_recipe function 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)
  6. Test your function using recipes.row(0) or some other row number, and then use it to add a new column with add_column.

Wrap-up (5 mins)​

  • Structured data groups multiple fields together.
  • We define structured types using @dataclass and class, and create instances by calling the constructor.
  • Fields are accessed with the dot notation.