Lesson 17 · Intermediate · 19 min

Python Nested Data

Work with JSON-like data one level at a time.

lists of dictionariesnested keystotals

An order is not a flat row: it has items, and each item has modifiers. Reading it in one jump is how KeyErrors are born. Which level should be checked first?

Real data rarely fits in a single list or dictionary. Orders contain items, API responses contain objects, and records contain fields. Nested data is easier when you name each level and access one step at a time. You will also guard each level, flatten records, and walk a tree with an explicit stack.

A list of dictionaries

A list holds the records; each dictionary holds the fields of one record. Loop over the list and read keys from each dictionary. This is the same shape you will see in many JSON files.

orders = [
    {"drink": "latte", "price": 4.5},
    {"drink": "tea", "price": 3.0},
]
for order in orders:
    print(order["drink"], order["price"])

Records in a list

Read nested keys

Use one pair of brackets per level. data["shop"] returns the inner dictionary; ["name"] then reads a field from it. Keep the intermediate expression in a variable when the path gets long.

data = {"shop": {"name": "Corner", "open": True}}
print(data["shop"]["name"])
print(data["shop"]["open"])

Two levels of keys

Update values and calculate totals

A nested value is still a normal dictionary value, so assignment works the same way. To total a field, loop over the outer list and read the key from each record. Avoid hard-coding indexes when a loop expresses the intent.

orders = [{"price": 3.0}, {"price": 4.5}]
orders[0]["price"] = 3.5
total = sum(order["price"] for order in orders)
print(total)

Update and total

Guard every level

A nested lookup is a chain of assumptions. Check the container type and the key at each level, or use get() with an empty default before indexing. One guard at the boundary is cheaper than catching TypeError and KeyError from three different places.

payload = {"shop": {"name": "Corner", "orders": [{"total": 9.0}]}}
shop = payload.get("shop", {})
orders = shop.get("orders", []) if isinstance(shop, dict) else []
first_total = orders[0].get("total") if orders and isinstance(orders[0], dict) else None
print(shop.get("name", "unknown"))
print(first_total)
print(payload.get("missing", {}).get("name", "unknown"))

Check the container before the key

Flatten records into rows

A nested order is convenient to store and awkward to report. Flatten one level at a time: outer order, inner item, then a row with both identifiers. Keep missing values explicit instead of dropping the row silently.

orders = [
    {"id": 1, "items": [{"name": "latte", "qty": 2}]},
    {"id": 2, "items": [{"name": "tea", "qty": 1}, {"name": "mocha", "qty": 3}]},
]
rows = []
for order in orders:
    for item in order.get("items", []):
        rows.append((order["id"], item["name"], item.get("qty", 0)))
for row in rows:
    print(row)

One loop per level, keep the parent id

Walk a tree with an explicit stack

When nesting can be deep, an explicit stack avoids recursion limits and makes pending work visible. Push children in reverse if you want to visit them left to right; the stack is last-in, first-out.

tree = {"name": "menu", "children": [
    {"name": "drinks", "children": [{"name": "latte"}, {"name": "tea"}]},
    {"name": "food", "children": [{"name": "toast"}]},
]}
stack = [tree]
while stack:
    node = stack.pop()
    print(node["name"])
    stack.extend(reversed(node.get("children", [])))

The stack holds the work that remains

Key points

  • Read nested data one bracket at a time.
  • A list of dictionaries is a common record collection.
  • Update a field by assigning to the key on the inner dictionary.
  • Use a loop or generator expression to total a field.
  • Nested data needs a guard per level; a stack makes a tree walk explicit.

Common mistakes

  • Using one bracket when the value is another dictionary.
  • Assuming every record has the same keys without checking.
  • Hard-coding indexes instead of looping over the records.
  • Pushing children in the wrong order and then wondering why output is reversed.
Your turn

Create two order dictionaries, put them in a list, then print the total price.

Try it yourself

Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.

In-browser PythonRuns locally · no account
First run downloads Pyodide from a CDN. Use Run code after editing.

Practice this lesson

12 questions with immediate explanations.

See all 12 answersStart practice
← moduleslambda & map →