Use |. It returns a new dictionary, and where both have the same key, the right-hand one wins.
defaults = {"theme": "light", "font_size": 14, "autosave": True}
user = {"theme": "dark", "font_size": 16}
settings = defaults | user
print(settings){'theme': 'dark', 'font_size': 16, 'autosave': True}That "right wins" rule is what makes this useful: put the defaults on the left and whatever the user chose on the right.
Changing one in place
|= (or the older update()) changes the left dictionary instead of making a new one:
cart = {"pen": 2}
cart |= {"notebook": 1, "pen": 5}
print(cart){'pen': 5, 'notebook': 1}On Python 3.8 or older
| arrived in Python 3.9. Before that, unpack both into a new dictionary. Same result, same "right wins" rule:
defaults = {"theme": "light", "font_size": 14}
user = {"theme": "dark"}
print({**defaults, **user}){'theme': 'dark', 'font_size': 14}The trap: nested dictionaries are replaced, not merged
The merge only looks at the top-level keys. If a value is itself a dictionary, the right-hand one replaces it whole:
defaults = {"editor": {"tabs": 4, "wrap": True}}
user = {"editor": {"tabs": 2}}
print(defaults | user){'editor': {'tabs': 2}}wrap is gone. For config with nested sections, merge each level yourself:
def deep_merge(base, override): merged = dict(base) for key, value in override.items(): if isinstance(value, dict) and isinstance(merged.get(key), dict): merged[key] = deep_merge(merged[key], value) else: merged[key] = value return merged defaults = {"editor": {"tabs": 4, "wrap": True}, "theme": "light"} user = {"editor": {"tabs": 2}} print(deep_merge(defaults, user))
{'editor': {'tabs': 2, 'wrap': True}, 'theme': 'light'}Adding up values instead of replacing them
If both dictionaries hold counts and you want them added, | is the wrong tool. Use Counter, which adds values on matching keys.