A dict's values can be any type — including other dicts. That gives you a two-level lookup table:
scores = {
"x": {"a": 1, "b": 2},
"y": {"a": 3, "b": 4},
}Two top-level keys ("x", "y"); each maps to a small dict with keys "a" and "b".
And to look up an inner value?
Chain the brackets:
print(scores["x"]["a"]) # 1
print(scores["y"]["b"]) # 4First bracket fetches the inner dict; second bracket fetches its value.
What about iteration — looking at every "a" across the top level?
A for over .items() gives you each top-level key and its inner dict:
for key, inner in scores.items():
print(key, inner["a"])
# x 1
# y 3And missing keys still raise KeyError?
At every level. scores["z"] raises; scores["x"]["c"] raises. With nested dicts, .get() is your friend for safe traversal — though we'll see better tools for this in week 4 (defaultdict).
data = {
"x": {"a": 1, "b": 2},
"y": {"a": 3, "b": 4},
}data["x"]["a"] # 1
data["y"]["b"] # 4Each [] does one lookup. The first one returns a dict; the second indexes into it.
KeyErrordata["z"] # KeyError: 'z'
data["x"]["c"] # KeyError: 'c'
data["z"]["a"] # KeyError: 'z' — fails at the first lookup.get()data.get("x", {}).get("a") # 1
data.get("z", {}).get("a") # None — "z" missing, falls back to {}
data.get("x", {}).get("c") # None — "x" exists but no "c"The {} default at each level keeps the chain alive when something's missing.
for key, inner in data.items():
print(key, inner)
# x {'a': 1, 'b': 2}
# y {'a': 3, 'b': 4}for outer_key, inner_dict in data.items():
for inner_key, value in inner_dict.items():
print(outer_key, inner_key, value)
# x a 1
# x b 2
# y a 3
# y b 4The nested loops run every combination — outer × inner.
Assignment works the same way:
data["x"]["a"] = 99
data["x"]["new"] = 5
data["z"] = {"a": 0, "b": 0} # add a whole inner dictUse them when one piece of data has a natural two-key index — e.g., "per-region per-day count." If you find yourself doing three levels of lookup constantly, consider whether a flat dict with tuple keys would be cleaner: data[(region, day)] = count.
if key in data and inner_key in data[key]:
value = data[key][inner_key]
else:
value = defaultWorks but is verbose. data.get(key, {}).get(inner_key, default) does the same in one line. (And defaultdict from week 4 makes building these even cleaner.)
A dict's values can be any type — including other dicts. That gives you a two-level lookup table:
scores = {
"x": {"a": 1, "b": 2},
"y": {"a": 3, "b": 4},
}Two top-level keys ("x", "y"); each maps to a small dict with keys "a" and "b".
And to look up an inner value?
Chain the brackets:
print(scores["x"]["a"]) # 1
print(scores["y"]["b"]) # 4First bracket fetches the inner dict; second bracket fetches its value.
What about iteration — looking at every "a" across the top level?
A for over .items() gives you each top-level key and its inner dict:
for key, inner in scores.items():
print(key, inner["a"])
# x 1
# y 3And missing keys still raise KeyError?
At every level. scores["z"] raises; scores["x"]["c"] raises. With nested dicts, .get() is your friend for safe traversal — though we'll see better tools for this in week 4 (defaultdict).
data = {
"x": {"a": 1, "b": 2},
"y": {"a": 3, "b": 4},
}data["x"]["a"] # 1
data["y"]["b"] # 4Each [] does one lookup. The first one returns a dict; the second indexes into it.
KeyErrordata["z"] # KeyError: 'z'
data["x"]["c"] # KeyError: 'c'
data["z"]["a"] # KeyError: 'z' — fails at the first lookup.get()data.get("x", {}).get("a") # 1
data.get("z", {}).get("a") # None — "z" missing, falls back to {}
data.get("x", {}).get("c") # None — "x" exists but no "c"The {} default at each level keeps the chain alive when something's missing.
for key, inner in data.items():
print(key, inner)
# x {'a': 1, 'b': 2}
# y {'a': 3, 'b': 4}for outer_key, inner_dict in data.items():
for inner_key, value in inner_dict.items():
print(outer_key, inner_key, value)
# x a 1
# x b 2
# y a 3
# y b 4The nested loops run every combination — outer × inner.
Assignment works the same way:
data["x"]["a"] = 99
data["x"]["new"] = 5
data["z"] = {"a": 0, "b": 0} # add a whole inner dictUse them when one piece of data has a natural two-key index — e.g., "per-region per-day count." If you find yourself doing three levels of lookup constantly, consider whether a flat dict with tuple keys would be cleaner: data[(region, day)] = count.
if key in data and inner_key in data[key]:
value = data[key][inner_key]
else:
value = defaultWorks but is verbose. data.get(key, {}).get(inner_key, default) does the same in one line. (And defaultdict from week 4 makes building these even cleaner.)
Create a free account to get started. Paid plans unlock all tracks.