Python Merge Dictionaries with Unpacking
python merge dictionaries with unpacking: Learn how to merge dictionaries in Python using ** unpacking, including order precedence, performance, and when to prefer the...
Merging dictionaries is a common operation in Python, and the ** unpacking syntax provides a concise way to combine them. This article explains how python merge dictionaries with unpacking works, what happens with duplicate keys, and when you should prefer other approaches.
How Dictionary Unpacking Works in a Literal
In Python 3.5 and later, you can use ** inside a dictionary literal to expand another mapping. The syntax is straightforward:
first = {"a": 1, "b": 2} second = {"b": 3, "c": 4} merged = {**first, **second} print(merged) # {'a': 1, 'b': 3, 'c': 4}
The ** operator unpacks each key-value pair from the source dictionary into the new literal. This creates a new dictionary object; the original dictionaries are not modified. The resulting dictionary contains all keys from both inputs, and for any duplicate keys, the value from the later unpacking wins.
Merge Order and Key Precedence
The order of unpacking determines which value is kept when keys collide. In the example above, "b" appears in both first and second, but second is unpacked after first, so its value 3 overrides 2. This right-to-left precedence is consistent with how later assignments override earlier ones in a literal.
a = {"x": 1, "y": 2} b = {"y": 10, "z": 20} merged = {**a, **b} print(merged) # {'x': 1, 'y': 10, 'z': 20}
If you reverse the order, the result changes:
merged = {**b, **a} print(merged) # {'y': 2, 'z': 20, 'x': 1}
In Python 3.7 and later, dictionaries preserve insertion order, so the key order in the merged result follows the order in which keys were first inserted during unpacking.
Merging More Than Two Dictionaries
Unpacking is not limited to two dictionaries. You can chain any number of ** expressions in a single literal:
base = {"a": 1} override = {"b": 2} extra = {"c": 3} merged = {**base, **override, **extra} print(merged) # {'a': 1, 'b': 2, 'c': 3}
This is often more readable than a series of dict.update() calls when you need a new dictionary. However, the number of dictionaries must be known at write time. If you have a dynamic list of dictionaries, you cannot use unpacking directly; you would need a loop or functools.reduce.
Using ** in Function Calls vs. Dict Literals
It is important to distinguish ** in a dictionary literal from ** in a function call. In a function call, ** unpacks a mapping into keyword arguments, and the keys must be strings:
def display(a, b): print(a, b) kwargs = {"a": 1, "b": 2} display(**kwargs)
In a dict literal, ** is purely for merging mappings. There is no requirement that keys be strings; any hashable key works. This distinction matters when you are merging dictionaries with non-string keys, such as integers or tuples.
Comparing with dict.update and the | Operator
Python offers several ways to merge dictionaries. The table below compares the most common approaches.
| Approach | Mutates original | Returns new dict | Python version | Readability |
|---|---|---|---|---|
{**a, **b} | No | Yes | 3.5+ | Good |
a.update(b) | Yes | No (returns None) | All | Moderate |
| `a | b` | No | Yes | 3.9+ |
dict(a, **b) | No | Yes | 3.5+ (limited) | Poor |
dict(a, **b) is a less-known variant that works only when b has string keys and a is a mapping. It is rarely used because it is less flexible and less readable.
Performance and Memory Characteristics
Using {**a, **b} creates a new dictionary and copies references to all keys and values. This is an O(n) operation, where n is the total number of key-value pairs. The memory footprint is proportional to the size of the merged result. If you need to merge many large dictionaries frequently, the repeated allocation of new dictionaries can become a concern.
dict.update() modifies the existing dictionary in place, which can be more memory-efficient if you do not need to preserve the original. However, it changes the original object, which may have side effects if that object is shared elsewhere. The | operator, like unpacking, returns a new dictionary and does not modify its operands.
For most use cases, the performance difference between these approaches is negligible. The choice should be driven by readability and whether you need to preserve the original dictionaries.
Compatibility and Version Considerations
The ** unpacking syntax in dict literals was introduced in Python 3.5 via PEP 448. If you are supporting Python 3.5 through 3.8, this is the cleanest way to merge dictionaries. The | operator was added in Python 3.9 (PEP 584). If your codebase targets Python 3.9 or newer, a | b is often more readable and less error-prone because it clearly signals a merge operation.
One subtle difference: a | b requires both operands to be dictionaries. {**a, **b} works with any mapping that can be unpacked, including dict, defaultdict, and Counter. If you need to merge a custom mapping type, unpacking is more flexible.
Edge Cases and Common Pitfalls
When merging dictionaries with unpacking, keep these points in mind:
- Nested dictionaries are shallow-copied. The merged dictionary contains references to the same nested objects. Modifying a nested value in the merged dictionary will affect the original dictionary if the value is mutable.
original = {"data": {"count": 1}} merged = {**original} merged["data"]["count"] = 2 print(original["data"]["count"]) # 2
- Keys can be any hashable type. Unlike function-call unpacking, dict-literal unpacking does not require string keys.
a = {1: "one"} b = {(2, 3): "tuple"} merged = {**a, **b} print(merged) # {1: 'one', (2, 3): 'tuple'}
-
Duplicate keys are resolved by later unpacking. This is expected but can be surprising if you assume the first dictionary wins.
-
Unpacking is not a deep merge. If you need to merge nested dictionaries recursively, you must implement that logic yourself or use a library like
deepmerge.
When to Choose Unpacking Over Alternatives
Use {**a, **b} when you want a new dictionary and your code must support Python 3.5 or later. It is also the right choice when you need to merge mappings with non-string keys or when you want to avoid the mutating behavior of dict.update().
Prefer the | operator when you are on Python 3.9+ and both operands are standard dictionaries. It is more explicit and less visually noisy, especially when chaining multiple merges:
merged = base | override | extra
Use dict.update() when you intentionally want to modify an existing dictionary in place, for example when building a configuration object incrementally. The choice ultimately comes down to whether you need a new object, which Python version you target, and how readable you want the merge operation to be.