How to merge two dictionaries in Python
Learn how to merge two dictionaries in Python. Explore different methods, tips, real-world applications, and how to debug common errors.

Merging two dictionaries in Python is a common task for developers. This operation combines key-value pairs from separate dictionaries, a vital skill for data manipulation and configuration management.
In this article, you’ll explore several techniques to combine dictionaries. You'll also find practical tips, real-world applications, and debugging advice to help you master the process.
Using the update() method
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
dict1.update(dict2)
print(dict1)--OUTPUT--{'a': 1, 'b': 2, 'c': 3, 'd': 4}
The update() method is a straightforward way to merge dictionaries. It modifies the original dictionary in place, which means dict1 is directly changed to include the contents of dict2. This approach is efficient when you don't need to preserve the original state of the first dictionary.
- In-place modification: The method alters the dictionary it's called on rather than creating a new one.
- Overwriting values: If a key exists in both dictionaries, the value from the dictionary being passed in will overwrite the original value.
Common dictionary merging techniques
If you'd rather not modify your original dictionaries as the update() method does, several other techniques can create a new, merged dictionary instead.
Using the ** unpacking operator
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
merged_dict = {**dict1, **dict2}
print(merged_dict)--OUTPUT--{'a': 1, 'b': 2, 'c': 3, 'd': 4}
The double-asterisk ** operator unpacks the key-value pairs from dict1 and dict2 into a new dictionary literal. This creates a completely new dictionary, merged_dict, leaving the original dictionaries untouched. It's a clean and readable way to combine dictionaries, especially since its introduction in Python 3.5.
- Key conflicts: If both dictionaries share a key, the value from the rightmost dictionary—in this case,
dict2—will overwrite the one from the first.
Using the dict() constructor
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
merged_dict = dict(dict1)
merged_dict.update(dict2)
print(merged_dict)--OUTPUT--{'a': 1, 'b': 2, 'c': 3, 'd': 4}
This approach is a two-step process that leaves your original dictionaries intact. First, you create a shallow copy of dict1 by passing it to the dict() constructor. Then, you call the update() method on this new dictionary to absorb the contents of dict2.
- This is a more explicit way to create a new merged dictionary compared to the
**operator, though it requires an extra line of code.
Using dictionary comprehension
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
merged_dict = {k: v for d in [dict1, dict2] for k, v in d.items()}
print(merged_dict)--OUTPUT--{'a': 1, 'b': 2, 'c': 3, 'd': 4}
Dictionary comprehension offers a concise and powerful way to merge dictionaries. This method iterates through a list of your dictionaries—in this case, [dict1, dict2]—and then loops through the key-value pairs of each one to build a new dictionary from scratch.
- This technique is highly flexible, allowing you to add conditional logic to filter or transform items during the merge.
- If keys overlap, the value from the last dictionary processed,
dict2, will overwrite any previous values.
Advanced dictionary merging techniques
Beyond the common methods, Python offers more specialized tools like collections.ChainMap and the | operator for advanced scenarios, including deep merging nested dictionaries.
Using collections.ChainMap
from collections import ChainMap
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'b': 4}
chain_map = ChainMap(dict2, dict1)
print(dict(chain_map))--OUTPUT--{'a': 1, 'b': 4, 'c': 3}
The collections.ChainMap class groups multiple dictionaries into a single, updatable view. Instead of creating an entirely new dictionary, it maintains a list of the original ones. This approach is memory-efficient when you don't need a separate merged copy.
- When you look up a key,
ChainMapsearches each dictionary in the order they are passed. The search stops as soon as the key is found. - In the example
ChainMap(dict2, dict1), the key'b'is found indict2first, so its value,4, is used. - The resulting
chain_mapis a live view, meaning any changes to the original dictionaries will be reflected in it.
Using the | operator (Python 3.9+)
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'b': 4}
merged_dict = dict1 | dict2
print(merged_dict)--OUTPUT--{'a': 1, 'b': 4, 'c': 3}
Introduced in Python 3.9, the pipe | operator offers a modern and concise way to merge dictionaries. It creates a new dictionary containing the key-value pairs from both dict1 and dict2, leaving the original dictionaries unmodified. This syntax is often preferred for its readability and simplicity.
- Handling duplicates: When a key exists in both dictionaries, such as
'b'in the example, the value from the right-hand dictionary,dict2, will be used in the final merged dictionary.
Deep merging nested dictionaries
def deep_merge(d1, d2):
result = d1.copy()
for k, v in d2.items():
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
result[k] = deep_merge(result[k], v)
else:
result[k] = v
return result
dict1 = {'a': 1, 'b': {'x': 10, 'y': 20}}
dict2 = {'c': 3, 'b': {'z': 30, 'y': 50}}
print(deep_merge(dict1, dict2))--OUTPUT--{'a': 1, 'b': {'x': 10, 'y': 50, 'z': 30}, 'c': 3}
A deep merge is what you'll need when dealing with nested dictionaries. Unlike the previous methods which perform a shallow merge—overwriting entire nested structures—a deep merge recursively combines them. The custom deep_merge function accomplishes this by intelligently handling key conflicts.
- When a key exists in both dictionaries and its values are also dictionaries, the function calls itself to merge the inner dictionaries.
- For all other keys, the value from the second dictionary simply overwrites the one in the first.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the dictionary merging techniques we've explored, Replit Agent can turn them into production-ready tools. You can use it to build applications that rely on combining data from multiple sources.
- Build a configuration management tool that merges a base config file with user-specific overrides using the
update()method. - Create a user profile dashboard that combines data from different sources, like social media and form inputs, into a single, unified view.
- Deploy a feature flag system that uses the
|operator orChainMapto layer environment-specific settings over a default configuration.
Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser.
Common errors and challenges
Merging dictionaries is usually simple, but a few common pitfalls can trip you up if you're not careful.
A key collision happens when the same key exists in multiple dictionaries you're trying to merge. Most methods, like the ** and | operators, resolve this by keeping the value from the last dictionary in the sequence. However, collections.ChainMap behaves differently, using the value from the first dictionary it encounters during a lookup, which can cause unexpected results.
The update() method is a common source of confusion because it modifies the original dictionary directly. If you need to keep your original dictionaries unchanged, this in-place modification can lead to bugs that are hard to track down. To prevent this, use methods that create a new dictionary, such as the ** unpacking operator.
Standard merging techniques perform a shallow merge, which means they only combine the top-level keys. If a key points to a nested dictionary, the entire inner dictionary from one will be overwritten by the other, instead of being merged recursively. This can cause you to lose valuable nested data, so you'll need a custom deep merge function to combine inner dictionaries correctly.
Handling key collisions when merging dictionaries
The order in which you merge dictionaries is crucial when dealing with key collisions. If you're not careful, you might accidentally overwrite important data, like a user's custom settings with default values. This is a common mistake in configuration management.
The code below illustrates this exact problem. Watch what happens to the user's preferences when they're merged with the default settings using the ** operator.
# Trying to add default settings while preserving user preferences
user_prefs = {'theme': 'dark', 'font_size': 12}
default_prefs = {'theme': 'light', 'font_size': 10, 'notifications': True}
merged_prefs = {**user_prefs, **default_prefs}
print(merged_prefs)
Because default_prefs comes last, its values for 'theme' and 'font_size' overwrite the user's choices, which is the opposite of what you want. The following code snippet shows how to fix this by reversing the order.
# Correct order: defaults first, then user preferences to override
user_prefs = {'theme': 'dark', 'font_size': 12}
default_prefs = {'theme': 'light', 'font_size': 10, 'notifications': True}
merged_prefs = {**default_prefs, **user_prefs}
print(merged_prefs)
By placing default_prefs first, you ensure its values are treated as the base. When user_prefs is unpacked second, its values for shared keys like 'theme' and 'font_size' overwrite the defaults. This simple change in order correctly prioritizes the user's settings. This is a common pattern in applications where you need to apply user-specific configurations over a set of default options, so always be mindful of the merge order.
Avoiding unintended dictionary modifications with update()
The update() method’s in-place modification is a common pitfall. Since it directly alters the original dictionary, you can unintentionally change data you meant to keep. This behavior often leads to subtle bugs that are difficult to trace. The code below shows this in action.
def add_settings(user_config, extra_settings):
user_config.update(extra_settings)
return user_config
original_config = {'debug': False, 'timeout': 30}
new_config = add_settings(original_config, {'verbose': True})
print("New config:", new_config)
print("Original config:", original_config) # Original is modified too!
The add_settings function directly alters the dictionary it receives. When you pass original_config to it, you're changing the original object, not a copy. The following code demonstrates how to prevent this from happening.
def add_settings(user_config, extra_settings):
return {**user_config, **extra_settings}
original_config = {'debug': False, 'timeout': 30}
new_config = add_settings(original_config, {'verbose': True})
print("New config:", new_config)
print("Original config:", original_config) # Original remains unchanged
The corrected add_settings function avoids this issue by using the ** unpacking operator. Instead of modifying the input dictionary, it creates and returns a completely new one that combines user_config and extra_settings. This approach leaves the original_config untouched, preventing unexpected side effects. Be mindful of this when passing dictionaries to functions where you need to preserve the original data, as in-place modifications can introduce hard-to-find bugs.
Merging nested dictionaries correctly
Standard merging techniques don't work as you'd expect with nested dictionaries. Instead of combining inner dictionaries, they perform a shallow merge, completely replacing one with the other. This often leads to unintended data loss. The following code demonstrates how this happens.
settings = {'display': {'theme': 'dark'}, 'audio': {'volume': 80}}
updates = {'display': {'font': 'Arial'}, 'audio': {'mute': False}}
merged = {**settings, **updates}
print(merged) # Nested dictionaries are replaced, not merged
You can see the 'theme' key is completely lost because the ** operator overwrites the entire 'display' dictionary. The following code shows how to properly merge the nested dictionaries to keep all your data intact.
def deep_merge(dict1, dict2):
result = dict1.copy()
for key, value in dict2.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
settings = {'display': {'theme': 'dark'}, 'audio': {'volume': 80}}
updates = {'display': {'font': 'Arial'}, 'audio': {'mute': False}}
merged = deep_merge(settings, updates)
print(merged)
The custom deep_merge function solves this by recursively combining nested dictionaries. It checks if a key exists in both dictionaries and if both values are also dictionaries. If they are, it merges the inner dictionaries instead of overwriting one with the other. This ensures no data is lost, like keeping the 'theme' key. You'll need this approach when dealing with complex configurations or structured data where preserving all nested information is critical.
Real-world applications
Beyond just avoiding errors, these merging techniques are fundamental to common tasks like managing application settings and aggregating data.
A classic use case for dictionary merging is managing application configurations. Most software needs a set of default settings to function correctly, but you also want to allow users to customize their experience. By merging a dictionary of default settings with a user's custom settings, you can create a final configuration that respects their choices.
The ** unpacking operator is particularly well-suited for this. By placing the default dictionary first, you ensure that any overlapping keys from the user's dictionary will overwrite the defaults. This pattern, {**defaults, **user_prefs}, is a clean and intentional way to build a final settings object that prioritizes user customization while providing sensible fallbacks.
Another common scenario is aggregating data from multiple API endpoints. Imagine building a user dashboard that pulls information from different microservices—one for user account details, another for recent activity, and a third for notification preferences. Each service returns its data as a separate dictionary.
To present a unified view to the user, you need to combine these dictionaries into a single object. A simple merge using the ** or | operators can create a flat dictionary to send to the frontend. If the data from different sources is nested and needs to be combined recursively, you'd use a custom deep merge function to ensure no information is lost.
Merging default and user configurations with ** operator
Here’s how you can use the ** operator to merge a default configuration with user-specific settings, creating a final configuration that prioritizes the user's choices.
default_config = {'debug': False, 'log_level': 'INFO', 'max_retries': 3}
user_config = {'log_level': 'DEBUG', 'timeout': 30}
final_config = {**default_config, **user_config}
print(final_config)
In this example, the ** operator unpacks both dictionaries into a new one called final_config. The original default_config and user_config dictionaries aren't changed. The resulting dictionary contains a mix of values from both sources.
- It includes the user's choice for
'log_level', which is'DEBUG'. - It adds the new
'timeout'setting from the user's configuration. - It keeps the original
'debug'and'max_retries'settings from the default configuration.
Combining data from multiple API responses
Here’s how you can combine data from separate API responses, like user details and order history, into a single, structured profile.
# Simulate responses from different API endpoints
user_data = {'id': 123, 'name': 'John Doe', 'email': '[email protected]'}
order_data = {'order_id': 456, 'user_id': 123, 'items': 5, 'total': 99.95}
# Merge the data to create a complete user profile
user_profile = {**user_data}
if user_data['id'] == order_data['user_id']:
user_profile['orders'] = {'count': order_data['items'], 'value': order_data['total']}
print(user_profile)
This code demonstrates a controlled merge, starting by creating a shallow copy of user_data to build the user_profile. It then validates that the order belongs to the user by checking if user_data['id'] matches order_data['user_id'].
Instead of simply combining all keys, the code selectively creates a new nested dictionary. It adds an 'orders' key to the profile and populates it with specific, renamed values from the order data. This targeted approach results in a clean, structured profile rather than a flat merge of all available information.
Get started with Replit
Turn your knowledge into a real tool. Tell Replit Agent to “build a config manager that merges default and user settings” or “create a dashboard that combines data from two APIs.”
The agent writes the code, tests for errors, and deploys your app. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.



%2520in%2520Python.png)