How to Deep Merge Nested Dicts Recursively in Python
Recursively merge two Python dictionaries, with overlay values taking precedence while preserving nested structures.
Python code
40 linesdef deep_merge(base, overlay):
"""
Recursively merge two dictionaries.
Values in 'overlay' take precedence over 'base'.
"""
result = base.copy()
for key, value in overlay.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge(result[key], value)
else:
result[key] = value
return result
if __name__ == "__main__":
base = {
"server": {
"host": "localhost",
"port": 8080,
"tls": {"enabled": False, "cert": "base.crt"}
},
"logging": {"level": "INFO", "file": "app.log"}
}
overlay = {
"server": {
"port": 9090,
"tls": {"enabled": True}
},
"logging": {"level": "DEBUG"},
"new_key": "added"
}
merged = deep_merge(base, overlay)
# Pretty-print for clear demonstration
import json
print(json.dumps(merged, indent=2))
Output
{
"server": {
"host": "localhost",
"port": 9090,
"tls": {
"enabled": true,
"cert": "base.crt"
}
},
"logging": {
"level": "DEBUG",
"file": "app.log"
},
"new_key": "added"
}
How it works
The deep_merge function starts by copying the base dictionary, ensuring the original isn't mutated. For each key-value pair in the overlay, it checks if the key already exists in the result and both values are dictionaries — if so, it recurses into the nested dicts. Otherwise, the overlay value simply overwrites or adds the key. This handles arbitrarily deep nesting because the recursion descends whenever both sides are dicts. The str() function in the print statement explicitly converts the output for display.
Common mistakes
- Forgetting to copy the base dict, which mutates the original input
- Not checking whether both values are dicts before recursing, leading to type errors
- Overwriting nested dicts entirely instead of merging them when keys overlap
Variations
- Use `collections.ChainMap` for one-level merges without nested recursion
- Add a guard to handle lists by concatenating or replacing them based on your use case
Real-world use cases
- Combining config files from different environments (dev, staging, prod) where later ones override defaults.
- Merging user-provided settings into an application's default configuration during startup.
- Applying incremental patches to API response payloads without losing unmodified nested fields.
Sponsored
More from Dictionaries & sets
- Build a Case-Insensitive Dict with a Wrapper Class in Python medium
- Build a defaultdict histogram of categories in Python easy
- Build adjacency dict graph from edges in Python easy
- Build an OrderedDict insertion order demo in Python 3 easy
- Check Invertible Mapping for Duplicate Values in Python easy
- Compare Two Dictionaries in Python easy
Keep learning
Related tutorials and quizzes for this topic.