Overview of a Basic Dictionary
A dictionary in Python is a built-in data structure that holds values with keys. It is essentially an unordered, mutable collection indexed by unique keys. Each key in the dictionary should be unique and should be any immutable data type, for example, strings, numbers, or tuples. However, its values may be of any data type and are not required to be unique.
Structure:
The basic structure of a dictionary looks like this:
my_dict = {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
Keys: The identifiers that allow you to access specific values.
Values: The data associated with each key.
person = {
"name": "Alice",
"age": 30,
"city": "New York"
}
In this example:
- “name”, “age”, and
"
city"
are the keys. "
Alice"
, 30, and “New York” are the corresponding values.
Dictionaries are a fundamental part of Python programming and are often used to represent data in a structured, accessible way.
Traditional Approach (Using update())
One common way to combine two dictionaries before Python 3.5 was using the update() method. The update() method allows you to merge key-value pairs from one dictionary into another. However, this changes the dictionary in-place: it updates the original dictionary.
Syntax:
dict1.update(dict2)
- dict1: The dictionary that will be updated.
- dict2: The dictionary whose key-value pairs will be added to dict1.
If dict2 contains keys that are already present in dict1, the values in dict2 will overwrite the values in dict1.
Example:
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict1.update(dict2)
print(dict1)
Output:
{'a': 1, 'b': 3, 'c': 4}
In this example,
‘a’ and ‘b’ keys already exist in dict1, the value of ‘a’ key is replaced with the value from dict2;.
A key-value pair ‘c’: 4 is added to dict1.
Limitation:
In-place modification:The original dict1 gets modified, which might always not be desirable if you aim to keep both dictionaries unchanged.
No direct expression: This method does not return a new dictionary, so it cannot be used in a single expression like modern methods.
For earlier versions of Python this was the most straightforward way to merge dictionaries methods.
Using the ** Operator (Python 3.5+)
Since Python 3.5, the unpacking operator ** has been allowed to combine dictionaries using a much more compact syntax. The ** operator performs the operation of unpacking key-value pairs from one or more dictionaries and merge them into a new dictionary. This approach does not affect the source dictionaries but instead returns a new dictionary as a result of this operation.
Syntax:
new_dict = {**dict1, **dict2}
- dict1: The first dictionary to merge.
- dict2: The second dictionary to merge.
- new_dict: The resulting merged dictionary.
If there are any duplicate keys between the dictionaries, the values from the last unpacked dictionary (dict2 in this case) will overwrite the earlier ones.
Example:
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
new_dict = {**dict1, **dict2}
print(new_dict)
Output:
{'a': 1, 'b': 3, 'c': 4}
In this example:
The key ‘b’ is common to both dict1 and dict2. The value for dict2, which is 3, overrides the value for dict1, which is 2.
This will yield a new dictionary, new_dict, containing all key-value pairs from both dictionaries.
Merging of Multiple Dictionaries:
You can combine more than two dictionaries in one expression using the ** operator:
dict1 = {'a': 1}
dict2 = {'b': 2}
dict3 = {'c': 3}
merged_dict = {**dict1, **dict2, **dict3}
print(merged_dict)
Output:
{'a': 1, 'b': 2, 'c': 3}
Advantages:
Immutability – It does not change the original dictionaries: dict1 and dict2.
Single Expression: This makes dictionaries mergeable in one expression; this also makes the code cleaner and more readable.
This is more flexible and concise than using update() and works well for creating a new dictionary from multiple sources.
Using the | Operator (Python 3.9+)
Since Python 3.9, an even more intuitive syntax-a new, intuitive syntax-is available to merge dictionaries: the | operator. With this operator, you will be able to merge-two dictionaries into a new one-in a really expressive and clean way, just like you would do with sets or any other collection.
Syntax:
new_dict = dict1 | dict2
dict1: The dictionary from which to merge the first.
dict2 – The second dictionary to merge.
new_dict: The resulting unified dictionary.
Like the ** unpacking operator, in case of a key collision (i.e., the same key present in both), the value from dict2 overrides the one from dict1. Unlike the update() method, however, the | operator does not modify either of the original dictionaries; instead, it returns a new dictionary.
Example:
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
new_dict = dict1 | dict2
print(new_dict)
Output:
{'a': 1, 'b': 3, 'c': 4}
In this example:
The key ‘b’ is common in both dictionaries, and its value from dict2 replaces the value from dict1, that is 2.
A new dictionary, new_dict, is created that includes the aggregation of key-value pairs.
Merging Multiple Dictionaries:
You can also combine more than two dictionaries using the | operator:
dict1 = {'a': 1}
dict2 = {'b': 2}
dict3 = {'c': 3}
merged_dict = dict1 | dict2 | dict3
print(merged_dict)
Output:
{'a': 1, 'b': 2, 'c': 3}
Comparison with Previous Techniques:
1.**update():
Modification: The update() modifies the dictionary in place, meaning the original dictionary itself gets changed.
Return Value: The update() method does not return a new dictionary.
Readability: The one above is less readable since it requires calling a
dict1.update(dict2)
2.**Unpacking:
Immutable : The ** operator returns a new dictionary, without touching the originals.
Readability While brief, it’s not as intuitive as the | operator, especially when one has to combine several dictionaries.
new_dict = {**dict1, **dict2}
3.|
Operator:
Immutable: Like **, the | operator returns a new dictionary without touching the originals.
Readability: The | operator is more readable and intuitive for the developers who are used to how the operator merges sets.
Efficiency: This is a straightforward and effective way, particularly when it comes to combining several dictionaries.
new_dict = dict1 | dict2
Why it is more readable and efficient:
Clarity: The | operator makes it crystal clear what the intention is, to combine a set of dictionaries. It is much clearer, hence easier to understand at first sight.
Single Expression: Like **, the | operator allows you to combine dictionaries in a single step, but its syntax is simpler and cleaner.
No Side Effects: The original dictionaries don’t get changed, so this is generally a safer approach to avoid unintended changes.
Conclusively, the | operator is more readable and efficient in general for people who put a price on clarity and code immutability.
Handling Key Conflicts
When joining two dictionaries, key conflicts are said to occur if both dictionaries have the same key but different values. In all cases, the second dictionary value will override the first dictionary value. This is similar for all merge methods: update(), the ** operator, and the | operator.
Behavior Across Methods:
Using update(): When a key is common in both dictionaries, the values from the second dictionary override those from the first dictionary.
Unpacking Operator: The same rule applies in that the values of the second dictionary override the values of the first dictionary.
Using the | Operator: The values in the second dictionary will override those in the first.
Example:
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
# Using update()
dict1.update(dict2)
print("After update():", dict1)
# Using ** unpacking operator
merged_dict1 = {**dict1, **dict2}
print("Using ** operator:", merged_dict1)
# Using | operator
merged_dict2 = dict1 | dict2
print("Using | operator:", merged_dict2)
Output:
After update(): {'a': 1, 'b': 3, 'c': 4}
Using ** operator: {'a': 1, 'b': 3, 'c': 4}
Using | operator: {'a': 1, 'b': 3, 'c': 4}
For example:
It contains ‘b’ as key in both dict1 and dict2.
Value of dict1[‘b’] was originally 2; after the merge dict2[‘b’] overrides the value with 3.
In all three cases – update(), **, and |, the effect is the same, that key ‘b’ has value 3 in the resulting dictionary.
How Key Conflicts Are Handled
- Order of Merge: The second dictionary’s values override the first in cases of key conflicts. The last dictionary unpacked or merged wins.
- No Errors: Unlike some operations in other programming languages, Python does not raise errors for key conflicts; it simply replaces the value.
This behavior ensures that merging is predictable, where the latest dictionary in the merge process determines the final value for conflicting keys.
Joining Several Dictionaries
You can combine more than two dictionaries in one expression using either the ** unpacking operator (Python 3.5+) or the | operator (Python 3.9+). Both approaches will return a single new dictionary that’s the result of their combination.
Using the ** Operator (Python 3.5+)
Another useful thing about the ** operator is that you can unpack multiple dictionaries in one line into one new dictionary:.
Example:
dict1 = {'a': 1}
dict2 = {'b': 2}
dict3 = {'c': 3}
merged_dict = {**dict1, **dict2, **dict3}
print(merged_dict)
Output:
{'a': 1, 'b': 2, 'c': 3}
This will include all key-value pairs from the three dictionaries combined into a new dictionary, merged_dict.
Using the | Operator (Python 3.9+)
The | operator provides an even more concise syntax to combine multiple dictionaries.
Example:
dict1 = {'a': 1}
dict2 = {'b': 2}
dict3 = {'c': 3}
merged_dict = dict1 | dict2 | dict3
print(merged_dict)
Output:
{'a': 1, 'b': 2, 'c': 3}
Handling Key Conflicts in Multiple Merges:
If there are key conflicts across the dictionaries, the values from the later dictionaries in the expression will override the earlier ones.
Example with Key Conflicts:
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict3 = {'c': 5, 'd': 6}
# Using ** operator
merged_dict1 = {**dict1, **dict2, **dict3}
# Using | operator
merged_dict2 = dict1 | dict2 | dict3
print("Using ** operator:", merged_dict1)
print("Using | operator:", merged_dict2)
Output:
Using ** operator: {'a': 1, 'b': 3, 'c': 5, 'd': 6}
Using | operator: {'a': 1, 'b': 3, 'c': 5, 'd': 6}
Both solutions handle key conflicts such that the last dictionary in the merge gets to decide the final value for any common keys, which in this case are ‘b’ and ‘c’.
Summary:
Both ** and | allow more than two dictionaries in the expression.
The | operator is more concise and intuitive, while ** is equally flexible.
Conflicts in key value are resolved in favor of the last dictionary provided in the merge.