A nested dictionary is a mapping whose values are themselves mappings (or mixed structures). That pattern shows up in JSON-style configuration, student records keyed by id, and API payloads. This tutorial walks through creating nested dicts, reading and updating inner fields, deleting safely, iterating, mixing lists as values, merging and flattening, and a few mistakes people hit with shared references.
If you are new to dicts in general, start with the Python dictionary guide. For flat merge rules (|, **, update), see merge dictionaries in Python. When you collect inner values into a list or view and only need “does any pass?” or “do all pass?” style answers, the built-in any() / all() helpers fit that pattern—see Python any() and all() (remember any(d) walks keys on a plain dict).
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
What is a nested dictionary in Python?
A nested dictionary is a dictionary inside a dictionary: some values are other dict objects, so you address inner data with chained keys such as outer["student1"]["age"]. The outer keys name groups (users, departments, locales); inner keys hold fields for each group. Python preserves insertion order in dicts (3.7+ in CPython); nesting does not change that, it only adds another layer of keys to navigate.
nested = {
"student1": {"name": "Ada", "age": 22},
"student2": {"name": "Lin", "age": 21},
}
print(nested["student1"]["name"])That prints Ada because nested["student1"] is a dict and ["name"] selects the inner field.
Create a nested dictionary in Python
You usually start from a literal, build with dict(), or assemble keys and values from lists.
Create a dictionary inside a dictionary
Use literals with braces: each value that should be a sub-record is written as its own {...}.
students = {
"s1": {"name": "Ada", "class": 12},
"s2": {"name": "Lin", "class": 11},
}
print(students)You get one top-level dict whose values are two inner dicts.
Create nested dictionary using dict()
dict() accepts keyword arguments only when keys are valid identifiers. For arbitrary string keys, pair iterables with zip or pass a list of (key, value) tuples.
by_keyword = dict(
s1={"name": "Ada", "age": 22},
s2={"name": "Lin", "age": 21},
)
keys = ["s1", "s2", "s3"]
profiles = [{"name": "Ada", "age": 22}, {"name": "Lin", "age": 21}, {"name": "Kim", "age": 20}]
by_zip = dict(zip(keys, profiles))
print(by_keyword["s1"]["name"], by_zip["s3"]["age"])That line prints Ada and 20, showing both construction styles.
Create nested dictionary from two lists
When one list holds outer ids and another holds matching inner dicts, zip plus dict is the usual idiom (same pattern as by_zip above).
ids = ["a100", "a101"]
rows = [{"role": "admin"}, {"role": "guest"}]
table = dict(zip(ids, rows))
print(table["a101"]["role"])The program prints guest.
Access values from a nested dictionary
Access inner dictionary values
Index from the outside in: first the outer key, then the inner key.
cfg = {"app": {"debug": True, "port": 8080}}
print(cfg["app"])
print(cfg["app"]["port"])The first print shows the whole inner dict; the second shows 8080.
Avoid KeyError while accessing nested values
Using [key] raises KeyError if any level is missing. Prefer .get per level with a default, or catch KeyError when you expect rare misses.
payload = {"user": {"name": "Ada"}}
print(payload.get("user", {}).get("email", "unknown"))
try:
_ = payload["org"]["id"]
except KeyError as e:
print("missing:", e)The first line prints unknown because email is absent. The try block prints a message showing which lookup failed.
Add or update values in a nested dictionary
Add a new inner dictionary
Assign a new key to a fresh dict, or assign an inner dict in one step.
db = {"s1": {"name": "Ada"}}
db["s2"] = {"name": "Lin", "age": 21}
print(db)db now has two top-level student entries.
Update value inside an inner dictionary
Assign through the full path to change one field without replacing the whole inner dict.
db = {"s1": {"name": "Ada", "age": 22}}
print("before:", db)
db["s1"]["age"] = 23
db["s1"]["city"] = "Pune"
print("after:", db)Age becomes 23 and a new inner key city appears under s1.
Delete items from a nested dictionary
Delete an inner dictionary
Use del on the outer key or pop if you want the removed value back.
db = {"s1": {"name": "Ada"}, "s2": {"name": "Lin"}}
removed = db.pop("s2")
print("removed:", removed)
print("left:", db)s2 disappears from db and removed holds the inner dict.
Delete a value from an inner dictionary
Remove one field inside a nested dict with del inner[key] or inner.pop("field").
db = {"s1": {"name": "Ada", "temp": True}}
del db["s1"]["temp"]
print(db)Only name remains under s1.
Loop through nested dictionary in Python
For control flow basics, see the Python for loop tutorial.
Loop through outer dictionary
items() gives (outer_key, inner_dict) pairs when values are dicts.
db = {"s1": {"name": "Ada"}, "s2": {"name": "Lin"}}
for sid, record in db.items():
print(sid, "->", record["name"])Each line prints one student id and their name.
Loop through inner dictionary values
Nest a second loop over each inner mapping’s items() when you need every field.
db = {"s1": {"name": "Ada", "age": 22}, "s2": {"name": "Lin", "age": 21}}
for sid, record in db.items():
print("---", sid)
for field, value in record.items():
print(field, value)You get each student block followed by name / age lines.
Nested dictionary with list values
Values do not have to be dicts only: a common shape is an outer key whose value is a list of dicts (rows, events, tasks).
dept = {
"name": "Platform",
"members": [{"name": "Ada", "level": "L5"}, {"name": "Lin", "level": "L4"}],
}
print(dept["members"][0]["name"])
print(len(dept["members"]))That prints the first member’s name and the list length. To append another row, use dept["members"].append({...}).
Merge nested dictionaries in Python
Shallow merge vs deep merge
Operators and methods such as |, {**a, **b}, and dict.update merge only the top level. If both dicts share a key whose value is another dict, the inner dict from the right-hand side replaces the inner dict from the left entirely (no automatic merge of inner keys).
import copy
left = {"user": {"name": "Ada", "score": 10}}
right = {"user": {"score": 99, "bonus": True}}
print("shallow |:", left | right)
deep_left = copy.deepcopy(left)
deep_left["user"].update(right["user"])
print("manual inner update:", deep_left)The shallow | result replaces the whole inner user dict with right’s version, so a field like name that only existed on the left disappears unless right defines it again. The deepcopy branch copies the left tree, then update merges keys inside user, so name can stay alongside updated scores and new keys.
For more patterns and ChainMap, see the merge section at the start of this page.
Flatten a nested dictionary
Flattening turns paths like a -> b -> c into one key such as a.b.c (or tuples) for CSV export or logging. Here is a compact recursive helper:
def flatten_dict(nested, parent_key="", sep="."):
out = {}
for key, value in nested.items():
path = f"{parent_key}{sep}{key}" if parent_key else key
if isinstance(value, dict):
out.update(flatten_dict(value, path, sep=sep))
else:
out[path] = value
return out
data = {"app": {"db": {"host": "localhost", "port": 5432}}, "debug": False}
print(flatten_dict(data))The flattened dict contains string keys like app.db.host and scalar values at the leaves.
Common mistakes with nested dictionaries
- Reusing the same inner dict literal for multiple outer keys: every outer slot points at one shared object, so mutating one path changes “all” rows. Build a new dict per slot (
copyof a template, dict comprehension, orcopy.deepcopy). - Assuming
{**a, **b}recursively merges inner dicts; it does not—plan shallow vs deep behavior explicitly. - Chaining
[k1][k2][k3]in production without validation; prefer.get, schema validation, or typed models for untrusted input.
Python nested dictionary quick reference table
| Task | Typical pattern |
|---|---|
| Read inner value | d["outer"]["inner"] |
| Safe read | d.get("outer", {}).get("inner", default) |
| Replace inner record | d["outer"] = {"k": "v"} |
| Patch one inner field | d["outer"]["field"] = value |
| Remove outer group | d.pop("outer") or del d["outer"] |
| Remove inner field | del d["outer"]["field"] |
| Iterate outer then inner | nested for over .items() |
| Shallow merge top level | a | b, {**a, **b}, a.update(b) |
| Deep copy tree | copy.deepcopy(d) |
| Flatten to dotted keys | recursive walk emitting parent.child keys |
Summary
Nested dictionaries model grouped records: outer keys identify rows or namespaces, inner keys hold fields. Create them with literals, dict, or zip of parallel lists; read and update with chained indices or safer .get; delete with del or pop at the right level; iterate with nested for loops over items(). When values are lists of dicts, index the list then the row dict. Merging and copying are shallow unless you use deepcopy or explicit inner updates, so inner dicts are the usual place bugs appear. Flatten with a short recursion when you need flat columns for export or display.
References
Frequently Asked Questions
1. What is a nested dictionary in Python?
2. How do I safely read a nested key that might be missing?
.get on each level, use try/except KeyError, or normalize once with collections.defaultdict so missing branches do not raise during reads.3. Does copying a nested dictionary copy the inner dictionaries too?
copy.deepcopy when you need independent copies of every nested level.4. How do I merge two nested dictionaries in Python?
|, update, and {**a, **b} only shallow-merge top-level keys; for recursive field-by-field merging you need a custom function or a library—see the merge section and the dedicated merge dictionaries guide linked from this page.
