063. Nested Data Structures
Compose data structures for complex models
063. Nested Data Structures
Each process in Aryan’s monitor is a dict with nested data:
proc = {
"name": "Chrome",
"pid": 4521,
"memory": {"rss_mb": 512.3, "vms_mb": 1024.0},
"threads": [
{"id": 101, "cpu_pct": 4.2},
{"id": 102, "cpu_pct": 2.1},
],
"tags": {"browser", "high_priority"},
}Accessing nested data requires chained indexing:
rss = proc["memory"]["rss_mb"] # dict → dict
first_tid = proc["threads"][0]["id"] # dict → list → dict
all_cpus = [t["cpu_pct"] for t in proc["threads"]]
total_cpu = sum(all_cpus)A whole snapshot is a list of these dicts, matching the shape of a real API response (e.g., psutil.process_iter() results serialized as JSON).
The bug that always trips beginners: sum(proc) on a dict iterates the string keys — TypeError. You need sum(proc["memory"].values()) or access the specific nested field.
💡 Fun fact: The “list of dicts” structure Aryan is using is identical to JSON (JavaScript Object Notation), the web’s universal data exchange format. When you call json.loads() on an API response, Python converts JSON objects to dicts and JSON arrays to lists — producing exactly this nested structure. This is why Python is so dominant for REST API clients and web scraping: the data arrives in JSON and is immediately usable as native Python dicts and lists.
⚠️ Watch out: Nested dicts and lists are passed by reference — modifying a nested object inside a function mutates the original. student["grades"].append(99) inside a helper function permanently changes the caller’s data. Use copy.deepcopy(student) to get a fully independent copy when you need to modify nested structures without affecting the original.
🤔 Think about it: average_grade(student) accesses student["grades"] assuming that key always exists. What happens if some students have a "grades" key and others don’t — perhaps new students with no grades yet? How would you make average_grade robust against missing or empty grades without crashing?
Learning objectives
- Access data in nested dicts and lists
- Build and process list-of-dicts records
- Navigate nested structures with multiple indexing levels
Key concepts
- nested data structures
- list of dicts
- nested access
Try it
Concept detail
Nested data structures combine lists and dicts for real-world models.
Common patterns: list of dicts — [{…}, {…}] — database rows, API responses, process snapshots dict of lists — {“alice”: [90,85], “bob”: [70,75]} — grouped data dict of dicts — {“config”: {“host”: “…”, “port”: 5432}} — nested config list of lists — [[1,2],[3,4]] — matrix / grid data
Accessing nested data: data[0][“name”] # first record’s name data[0][“grades”][2] # first record’s third grade config[“db”][“host”] # two levels of dict
Building nested structures: [{“x”: i, “y”: i*2} for i in range(5)]
Mutation warning: changing a nested dict mutates the original because dicts are passed by reference. Use copy.deepcopy() for true independence.
This shape (list of dicts) is identical to JSON — json.loads() and json.dumps() convert between Python nested structures and JSON strings directly.
Solution
def average_grade(student):
grades = student["grades"]
return sum(grades) / len(grades)
def top_student(students):
best = None
best_avg = -1
for student in students:
avg = average_grade(student)
if avg > best_avg:
best_avg = avg
best = student["name"]
return best
def students_above(students, threshold):
result = []
for student in students:
if average_grade(student) >= threshold:
result.append(student["name"])
return resultTests
STUDENTS = [
{"name": "Alice", "grades": [90, 85, 92]},
{"name": "Bob", "grades": [70, 75, 68]},
{"name": "Charlie", "grades": [95, 98, 100]},
]
def test_average_alice():
assert abs(average_grade(STUDENTS[0]) - 89.0) < 0.01
def test_average_bob():
assert abs(average_grade(STUDENTS[1]) - 71.0) < 0.01
def test_top_student():
assert top_student(STUDENTS) == "Charlie"
def test_top_student_returns_string():
result = top_student(STUDENTS)
assert type(result) == str # must be the name, not the whole dict
def test_students_above():
result = students_above(STUDENTS, 80)
assert "Alice" in result
assert "Charlie" in result
assert "Bob" not in result
def test_students_above_none():
assert students_above(STUDENTS, 99) == []