Codectionary / Developer documentation / Python

Merging dictionaries

Combine settings with explicit precedence and understand the difference between copying and updating.

How it works

For dictionaries, | creates a merged dictionary and |= updates the left-hand dictionary in place. These operators were added in Python 3.9. When keys collide, the right-hand value wins.

The merge is shallow: nested objects are not recursively merged or deep-copied. Dictionary unpacking with {**left, **right} is another way to build a new dictionary with rightmost precedence.

Limitations and practical choices

Define precedence deliberately when combining defaults, configuration and explicit overrides.

A top-level merge of nested settings can replace an entire nested dictionary. Write an explicit nested policy when that is not intended.

Try it yourself

Combine default page size and theme with user overrides. Then nest the display settings and observe that a shallow merge does not combine their child keys.

Syntax

combined = defaults | overrides
settings |= overrides

Examples

Overlay preferences

Preserve defaults while producing effective settings.

defaults = {"theme": "light", "size": 20}
overrides = {"theme": "dark"}
effective = defaults | overrides
print(effective)
print(defaults)
  1. The theme key appears on both sides, so dark wins. size appears only on the left and is retained.
  2. | creates a new dictionary; printing defaults shows that the original top-level settings were not changed.
  3. Use this for a simple configuration overlay when callers still need the original defaults.
  4. Expected output: {'theme': 'dark', 'size': 20} {'theme': 'light', 'size': 20}

Update an existing mapping

Make an intentional in-place change.

settings = {"retries": 1}
same_settings = settings
settings |= {"retries": 3, "timeout": 10}
print(same_settings)
  1. same_settings refers to the same dictionary. |= updates that dictionary rather than creating a separate merged result.
  2. The alias observes the changed retry count and added timeout. This is why mutation should be deliberate.
  3. Use an in-place update only when all holders of the mapping should see the new values.
  4. Expected output: {'retries': 3, 'timeout': 10}

Expose a shallow merge

Avoid assuming nested settings combine automatically.

defaults = {"display": {"theme": "light", "size": 20}}
overrides = {"display": {"theme": "dark"}}
combined = defaults | overrides
print(combined)
  1. The collision is on the top-level key display. Its entire value is replaced by the right-hand dictionary.
  2. The missing size is not a bug in the operator: no recursive merge was requested. An application must define how nested values should combine.
  3. Test nested configuration overlays explicitly rather than relying on an assumed deep-merge rule.
  4. Expected output: {'display': {'theme': 'dark'}}

Best practices

  • Define precedence deliberately when combining defaults, configuration and explicit overrides.
  • A top-level merge of nested settings can replace an entire nested dictionary. Write an explicit nested policy when that is not intended.

Runtime & versions

Python interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

Python language reference

Specifications & further reading