Week 3 of 8

Jupyter: Dictionaries & Files

Test JSON reading and writing hands-on, see the list repetition gotcha, and explore nested data

Day 12 60 minutes Read + Experiment

Day 12 of 40

What You'll Accomplish Today

Part 1: Read (15 min)

Open the Real Python JSON guide and skim it. You don't need to read every word — focus on the two sections that matter most right now.

What to read Focus on
Real Python — Working with JSON in Python Skim the first half. Focus on json.load() (reading from file) and json.dump() (writing to file). Skip the json.loads() / json.dumps() string variants for now — those are for API responses, which comes in Week 5.
Skim vs. Read

You watched the Corey Schafer videos yesterday. This article covers the same concepts in text. Skim quickly — you're looking for anything that was unclear from the video. If you already feel solid on json.load and json.dump, spend 5 minutes max and move on to Jupyter.

Part 2: Jupyter Experiments (45 min)

Open Jupyter (jupyter notebook in your terminal) and create a new notebook called week-3-experiments.ipynb. Write each cell below in order. Before running each cell, predict the output. Getting it wrong teaches you more than getting it right.

Cell 1 — Dictionary Basics

week-3-experiments.ipynb — Cell 1 Python
shot = {
    "scene": 1,
    "description": "Wide establishing, golf course sunrise",
    "platform": "Kling",
    "tags": ["wide", "outdoor", "golden hour"]
}
print(shot["description"])
print(shot.get("mood", "none"))
print(shot.keys())
print(shot.values())
print(shot.items())

Things to notice:

shot["tags"] would give you the list ["wide", "outdoor", "golden hour"] — a dictionary value can be a list, a number, another dictionary, or anything else. There's no limit.

.get("mood", "none") — the key "mood" doesn't exist in this dictionary, so .get() returns the default value "none" instead of crashing.

.keys(), .values(), .items() return view objects (not lists). They show what's in the dictionary and are iterable — you can use them in for loops. .items() is the one you'll use most often, because it gives you both key and value at once.

Cell 2 — Nested Data

week-3-experiments.ipynb — Cell 2 Python
project = {
    "title": "Golf Commercial",
    "client": "Augusta National",
    "scenes": [
        {"scene": 1, "shots": 3, "location": "Fairway"},
        {"scene": 2, "shots": 5, "location": "Clubhouse"},
        {"scene": 3, "shots": 2, "location": "Putting green"},
    ]
}
print(project["scenes"][1]["location"])
for scene in project["scenes"]:
    print(f"  Scene {scene['scene']}: {scene['shots']} shots at {scene['location']}")

Read the nesting carefully:

project["scenes"] returns the list of three scene dictionaries.

project["scenes"][1] returns the second scene dictionary (index 1 = the second item).

project["scenes"][1]["location"] returns the "location" value from that scene — "Clubhouse".

This is how real-world data is structured. An API response from any AI service — Kling, Runway, Veo — will return nested JSON like this. Being comfortable reading and navigating nested structures is essential. Practice "reading inward" from left to right: project → scenes list → second item → location field.

Cell 3 — Dictionary Comprehension and zip()

week-3-experiments.ipynb — Cell 3 Python
platforms = ["Kling", "Runway", "Veo"]
counts = {p: 0 for p in platforms}
print(counts)

shot_nums = [1, 2, 3]
descriptions = ["Wide establishing", "Close-up hands", "Aerial reveal"]
shot_map = dict(zip(shot_nums, descriptions))
print(shot_map)

Dictionary comprehension: {p: 0 for p in platforms} creates a dictionary in one line — for each platform in the list, create a key with value 0. The output is {"Kling": 0, "Runway": 0, "Veo": 0}. This is the Pythonic way to initialize a counting dictionary.

zip(): takes two lists and pairs them up element by element. zip([1, 2, 3], ["a", "b", "c"]) gives you [(1, "a"), (2, "b"), (3, "c")]. Wrapping it in dict() converts those pairs into a dictionary. Useful when you have parallel lists that should be combined.

These are "advanced one-liner" tools. Don't worry about memorizing them today — just recognize the patterns when you see them.

Cell 4 — Reading and Writing Files

week-3-experiments.ipynb — Cell 4 Python
import json

test_data = [
    {"name": "Shot A", "platform": "Kling"},
    {"name": "Shot B", "platform": "Runway"},
]

with open("test_output.json", "w") as f:
    json.dump(test_data, f, indent=2)
print("File written!")

with open("test_output.json", "r") as f:
    loaded = json.load(f)

print(f"Loaded {len(loaded)} items:")
for item in loaded:
    print(f"  {item['name']} → {item['platform']}")

After running this cell, navigate to your notebook's folder in File Explorer and open test_output.json in a text editor. You'll see the data sitting there — structured, human-readable JSON. That's persistence. That data survives script restarts, computer reboots, and anything else. You wrote it once and it stays.

The write block ("w" mode) and the read block ("r" mode) are completely independent — they just happen to reference the same file. In a real script, the write would happen when the user adds data; the read would happen when the script starts. Together, they form a complete save/load cycle.

indent=2 makes the file pretty-printed with 2-space indentation. Without it, the file would be written on one line — valid JSON, but unreadable to humans. Always use indent=2.

Cell 5 — try/except for Missing Files

week-3-experiments.ipynb — Cell 5 Python
import json

try:
    with open("nonexistent_file.json", "r") as f:
        data = json.load(f)
except FileNotFoundError:
    print("File not found — starting with empty list.")
    data = []

print(f"Data has {len(data)} items.")

Why this matters: The first time a user runs your Prompt Vault script, prompts.json doesn't exist yet — it hasn't been created. Without try/except, Python crashes with FileNotFoundError and the user sees a confusing error message. With it, you catch the error and handle it gracefully: start with an empty list instead of crashing. The user doesn't even know the file was missing.

The structure: try: is the code you want to run. except FileNotFoundError: is the fallback that runs only if that specific error occurs. Any other error (wrong JSON format, permission denied) would still crash — you're only catching the specific case you know how to handle.

Memorize This Pattern

This try/except + fallback to empty list is the pattern used in every project that saves data to files. You will write this in every single build project for the rest of the course. Get it into muscle memory now:

try:
    with open("data.json", "r") as f:
        data = json.load(f)
except FileNotFoundError:
    data = []

Four lines. Try to load the file. If it doesn't exist, start with an empty list. That's it.

End of Day Checklist

Tomorrow

You build the Prompt Library — a real JSON-backed tool that stores AI video prompts to disk and loads them on startup. It's the first project where your data survives between sessions. You'll feel the difference the moment you run the script a second time and see your previous entries still there.