Courses/Python Mastery/Module 5: Data Structures
Module 5 · Lesson 425 minBeginner

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

Lesson goal
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

code
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:

code
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

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

Output:

code
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

code
# 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

code
# 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)

code
# 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

  • How do you get Diya's marks from the students list? *(students[1]["marks"])*
  • What's the loop pattern for a list of dicts? *(for item in list: item["key"])*
  • data["forecast"]["monday"] — describe each step. *(dict → value is a dict → key)*
  • 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.