Nested Dictionary Comprehension in Python
A nested dictionary comprehension is simply a dict comprehension whose value expression is another dict comprehension — it lets you build, reshape, or flatten a dict of dicts in a single statement. The pattern is powerful for restructuring data pulled from JSON APIs or grouped records, but it hits a readability wall fast, so knowing when to fall back to a loop matters as much as the syntax.
Quick answer: To build a dict of dicts, put an inner comprehension in the value slot: {k: {ik: iv for ik, iv in inner} for k, inner in data}. To flatten one, use two for clauses in a single comprehension: {(ok, ik): v for ok, inner in d.items() for ik, v in inner.items()}. Outer loop comes first, left to right.
How do I build a dict of dicts with a comprehension?
Nest a second comprehension in the value position of the outer one. The outer comprehension produces the top-level keys; for each of them, the inner comprehension builds a fresh dictionary. This is the go-to pattern for transforming every inner value in one pass — here, converting prices in a nested structure to a different currency.
prices_usd = {
"fruit": {"apple": 1.20, "cherry": 3.80},
"veg": {"carrot": 0.60, "kale": 2.10},
}
RATE = 0.92
prices_eur = {category: {item: round(usd * RATE, 2)
for item, usd in items.items()}
for category, items in prices_usd.items()}
print(prices_eur)
# {'fruit': {'apple': 1.1, 'cherry': 3.5}, 'veg': {'carrot': 0.55, 'kale': 1.93}}
You can also generate a grid from scratch — say, a multiplication table — by nesting over two ranges:
table = {a: {b: a * b for b in range(1, 4)} for a in range(1, 4)}
print(table[2][3]) # 6
print(table)
# {1: {1: 1, 2: 2, 3: 3}, 2: {1: 2, 2: 4, 3: 6}, 3: {1: 3, 2: 6, 3: 9}}
How do I flatten a nested dictionary with a comprehension?
Flattening uses a different shape: one comprehension with two for clauses, not a comprehension inside a comprehension. The clauses read left to right like nested loops — outer first, inner second — and each inner item produces one entry in a single flat dictionary.
prices_usd = {
"fruit": {"apple": 1.20, "cherry": 3.80},
"veg": {"carrot": 0.60, "kale": 2.10},
}
# Option 1: tuple keys
flat = {(cat, item): usd
for cat, items in prices_usd.items()
for item, usd in items.items()}
print(flat[("fruit", "apple")]) # 1.2
# Option 2: joined string keys
flat = {f"{cat}.{item}": usd
for cat, items in prices_usd.items()
for item, usd in items.items()}
print(flat)
# {'fruit.apple': 1.2, 'fruit.cherry': 3.8, 'veg.carrot': 0.6, 'veg.kale': 2.1}
Getting the clause order backwards is the classic mistake — if you write the inner loop first, you get a NameError because items is not defined yet. When in doubt, write it as a nested for loop first, then copy the loop headers into the comprehension in the same order.
How do I transpose (swap inner and outer keys) a nested dict?
Transposing turns d[a][b] into d[b][a] — for example, flipping {city: {year: population}} into {year: {city: population}}. A nested comprehension handles it when every inner dict shares the same keys: iterate the new outer keys, then rebuild each inner dict by indexing into the original.
pop_by_city = {
"austin": {2023: 979, 2024: 986},
"denver": {2023: 716, 2024: 720},
}
pop_by_year = {year: {city: data[year]
for city, data in pop_by_city.items()}
for year in next(iter(pop_by_city.values()))}
print(pop_by_year)
# {2023: {'austin': 979, 'denver': 716}, 2024: {'austin': 986, 'denver': 720}}
If the inner dicts have ragged keys (not every city reported every year), the comprehension version raises KeyError or silently drops data depending on how you write it. That is a signal to switch to a loop with setdefault():
pop_by_year = {}
for city, data in pop_by_city.items():
for year, pop in data.items():
pop_by_year.setdefault(year, {})[city] = pop
When should I refactor a nested comprehension into a loop?
Refactor as soon as any of these appear: three or more levels of nesting, a filtering if on both levels, error handling needs (missing keys, bad types), or a line you have to horizontally scroll. Comprehensions cannot contain try/except, cannot log progress, and give unhelpful tracebacks — a loop costs three extra lines and buys you all of that back.
# Dense and fragile:
result = {k: {ik: f(iv) for ik, iv in inner.items() if iv is not None}
for k, inner in raw.items() if inner}
# Same logic, debuggable:
result = {}
for k, inner in raw.items():
if not inner:
continue
cleaned = {}
for ik, iv in inner.items():
if iv is not None:
cleaned[ik] = f(iv)
result[k] = cleaned
A good rule of thumb: a nested comprehension should express one reshaping idea (transform, flatten, or transpose). The moment it expresses two, split it. For the single-level building blocks these patterns are made of, see dict comprehensions in Python, and if your keys and values need per-item conditional logic, combine these shapes with the techniques in Python dictionary methods such as setdefault() and get().
Pro Tip: The two shapes are easy to confuse: a comprehension inside the value slot builds a dict of dicts, while two for clauses side by side flatten one. If your output should be nested, nest the braces; if it should be flat, chain the fors — and always order chained fors outer-to-inner, exactly as you would write the loops.