Unit 08 · lesson

Dictionaries and Structured Data

Core path: 30 minutes

What if position is the wrong way to describe the meaning of the data?

A list is great when order is important.

But consider one player stored as:

player = ["Nova", 850, "Elite", True]

What does this mean?

player[2]

You have to remember the hidden schema:

0 → name
1 → score
2 → rank
3 → online

The program technically has the data. The meaning is buried in positions.

A dictionary lets the labels become part of the structure.

A dictionary maps keys to values

player = {
    "name": "Nova",
    "score": 850,
    "rank": "Elite",
    "online": True,
}

Read one entry as:

key       value
"score" → 850

Then:

player["score"]

asks for the value associated with the key "score".

That access expression communicates more intent than player[1] when the position itself has no meaningful role.

Keys are labels, not sequence positions

Lists answer questions such as:

What is the item at position 2?

Dictionaries answer questions such as:

What value is stored under the label score?

Those are different relationships.

This is why choosing a structure begins with the shape of the problem, not with which syntax you learned most recently.

Access, modify, and add fields

print(player["name"])

Modify an existing key:

player["score"] = 900

Add a new key/value pair:

player["team"] = "Zero Gravity"

After those updates, the dictionary's state changed in place.

Like lists, dictionaries are mutable.

A missing key is different from a value being empty

This:

player["team"]

raises KeyError if the key does not exist.

That is different from:

{"team": ""}

where the key exists and its value happens to be an empty string.

Those two states can mean different things in an application:

missing key → field not supplied / schema mismatch
empty value → field exists but contains no text

Do not collapse them automatically.

.get() can provide a defined fallback

team = player.get("team", "No team")

If "team" is missing, the result becomes "No team" instead of raising KeyError.

That is useful when a missing field is expected and you have a real fallback behavior.

Do not use .get() everywhere just to hide missing-key bugs. If the program requires score to exist, silently substituting a default might make bad data harder to detect.

Looping through dictionaries depends on what you need

Loop through keys:

for key in player:
    print(key)

Loop through key/value pairs:

for key, value in player.items():
    print(key, value)

Again, choose the form that matches the question.

If you need the labeled fields and their values, .items() makes that relationship explicit.

One record versus many records

A dictionary works well for one player:

player = {
    "name": "Nova",
    "score": 850,
}

A roster contains many players, so an outer list is natural:

players = [
    {"name": "Nova", "score": 850},
    {"name": "Maya", "score": 940},
]

Read the structure from the outside in:

players
  ↓ list
multiple player records
  ↓ each item is a dictionary
one player's named fields

Then:

for player in players:
    print(f"{player['name']}: {player['score']}")

The loop selects one dictionary at a time.

The key lookup selects one field from that dictionary.

Two structures. Two jobs.

Run the same list-of-dictionaries pattern and inspect which records survive the filter:

filter_robots.py
OutputRun with button or Ctrl/Cmd+Enter
Run the code to see output.
Ready to edit. Press Run when you want evidence.

This is a small data-filter pattern: inspect each record, test one field, then act only on matching records.

Change one robot's online value, run again, and explain the result by naming both structure layers: the outer list supplies records, then the dictionary key supplies the field used by the branch.

Nested data looks complicated until you name each layer

Consider:

team = {
    "name": "Zero Gravity",
    "members": [
        {"name": "Nova", "role": "programmer"},
        {"name": "Maya", "role": "driver"},
    ],
}

Do not read the whole expression at once.

Ask:

team → dictionary
team["members"] → list
team["members"][0] → first member dictionary
team["members"][0]["name"] → "Nova"

Nested data is just multiple known structures combined.

When an access expression looks intimidating, trace one layer at a time.

Why this matters beyond Week 8

You will see list/dictionary combinations again in:

  • JSON files;
  • API responses;
  • configuration data;
  • test cases;
  • structured records before they become objects.

Week 9 saves them.

Week 13 receives them from APIs.

Week 12 asks when some records should become objects with behavior.

So this is not a one-week syntax trick. It becomes a common representation of application data.

Inspect one API-shaped record before we ever call an API

response_data = [
    {"id": 1, "title": "inspect logs", "completed": False},
    {"id": 2, "title": "run tests", "completed": True},
]

Before running code, answer:

top-level type:
number of records:
type of response_data[0]:
value of response_data[0]["title"]:
value of response_data[1]["completed"]:

Then write:

for task in response_data:
    if not task["completed"]:
        print(task["title"])

The output should identify only unfinished tasks.

That is almost exactly the structural reasoning you will use with real JSON from an API later.

Decide from the relationship

For each case, choose the structure and explain the relationship it represents:

A robot callsign

Ordered characters → string.

A sequence of five lap times

Ordered collection → list.

One robot's name, battery, and mode

Named fields → dictionary.

A fleet of robot records

Collection of named records → likely list of dictionaries.

There can be other valid designs. Defend the one you choose.

Vocabulary lab

Flip the idea, not just the card

Explain the term before you reveal the back. Then compare your explanation with the definition, example, and warning.

1 / 5
Read all terms without animation
Dictionary
A mutable Python mapping that associates unique keys with values. Example: {'name': 'Nova', 'score': 850}. Do not confuse it with: A list that primarily identifies items by numeric position.
Key
A label used to locate a value in a mapping. Example: 'score' in player['score']. Do not confuse it with: The value 850 stored under that key.
KeyError
An exception raised when direct dictionary access requests a key that is not present. Example: player['team'] when the team key does not exist. Do not confuse it with: A key that exists with an empty value.
Nested Data
A structure containing other structures as values or items. Example: A list of player dictionaries. Do not confuse it with: A single scalar value such as one integer.
Mapping
A relationship where keys identify associated values rather than relying primarily on numeric sequence positions. Example: A dictionary mapping 'battery' to 82. Do not confuse it with: An ordered sequence such as a list.