Tech With Galvan
Courses/Python Mastery/Data Structures

Nesting: Lists of Dicts (the Real-World Shape)

25 min • Beginner • Module 5

Handle the nested structures that APIs and databases actually return.

Nesting: Lists of Dicts (the Real-World Shape)

Solo containers are toys. Real data combines them — and one combination appears so often it deserves its own lesson: a list of dictionaries. Master this shape and every API, database export, and JSON file on the internet becomes readable to you.

The shape

students = [
    {"name": "Aarav", "age": 16, "marks": 92},
    {"name": "Diya",  "age": 15, "marks": 95},
    {"name": "Kabir", "age": 16, "marks": 78},
]

Read it inside-out: it’s a list (so: loop it, index it) whose items are dicts (so: access by key). Two-step access:

print(students[0]["name"])       # Aarav — first student, then their name
print(students[2]["marks"])      # 78

students[0] picks the first dict; ["name"] reaches inside it. Left to right, outside in.

Looping the shape — the pattern of the course

for student in students:
    print(f"{student['name']} scored {student['marks']}")

Output:

Aarav scored 92
Diya scored 95
Kabir scored 78

Each pass, student is one dict — so inside the loop you use dict access. This loop is the pattern behind every dashboard, every leaderboard, every API display you’ll build. Burn it into memory.

Real queries on the shape

# Find the topper
topper = students[0]
for s in students:
    if s["marks"] > topper["marks"]:
        topper = s
print(f"Topper: {topper['name']} ({topper['marks']})")

# Everyone above 80
for s in students:
    if s["marks"] > 80:
        print(s["name"], "— distinction!")

# Average marks
total = 0
for s in students:
    total += s["marks"]
print(f"Average: {total / len(students):.1f}")

Three real business questions, answered with loops you already know. The data shape changes nothing about your logic — it just tells you which keys to reach for.

Adding and removing — both levels

# Add a new student (a new dict into the list)
students.append({"name": "Meera", "age": 15, "marks": 88})

# Update one field of one student
students[1]["marks"] = 97        # Diya's marks updated

# Add a new field to every student
for s in students:
    s["grade"] = "A" if s["marks"] > 80 else "B"

Other nestings you’ll meet (quick tour)

# dict of lists — one key, many values
marks_by_subject = {
    "math": [92, 85, 78],
    "science": [88, 91, 74],
}
print(marks_by_subject["math"][0])     # 92 — dict key, then list index

# dict of dicts — lookup by name
by_name = {
    "Aarav": {"age": 16, "marks": 92},
    "Diya": {"age": 15, "marks": 95},
}
print(by_name["Diya"]["marks"])        # 95

The reading rule is universal: go layer by layer, outside in, and at each layer ask “am I at a list (index) or a dict (key)?”

Common Errors & Fixes

  • TypeError: list indices must be integers — you used ["name"] on the list instead of a dict. First index the list (students[0]), then the key.
  • KeyError inside a loop — one of the dicts lacks that key. Use .get("key") or fix the data.
  • IndexError in a nested list — the inner list is shorter than you assumed; len() it first.

✅ Checkpoint

  1. How do you get Diya’s marks from the students list? (students[1][“marks”])
  2. What’s the loop pattern for a list of dicts? (for item in list: item[“key”])
  3. data["forecast"]["monday"] — describe each step. (dict → value is a dict → key)
  4. Add a "passed": True field to every student in a loop. (for s in students: s[“passed”] = True)

Module 5 checkpoint reached — lists, tuples, sets, dicts, and nesting. The entire container toolbox.

Next module: Control Flow — where your data starts making decisions.