forked from huangsam/ultimate-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdict_union.py
More file actions
174 lines (141 loc) · 6.31 KB
/
dict_union.py
File metadata and controls
174 lines (141 loc) · 6.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
"""
Dictionary union operators allow you to merge dictionaries using
the | (union) and |= (in-place union) operators. These operators
provide a clean and intuitive syntax for combining dictionaries.
This feature was introduced in Python 3.9 through PEP 584. Before
this, you had to use methods like dict.update() or {**dict1, **dict2}
syntax to merge dictionaries.
"""
def main() -> None:
# Traditional dictionary merging before Python 3.9
# Method 1: Using dict.update() (modifies the original)
dict1_old = {"a": 1, "b": 2}
dict2_old = {"c": 3, "d": 4}
dict1_old.update(dict2_old)
assert dict1_old == {"a": 1, "b": 2, "c": 3, "d": 4}
# Method 2: Using dictionary unpacking (creates a new dict)
dict3_old = {"a": 1, "b": 2}
dict4_old = {"c": 3, "d": 4}
merged_old = {**dict3_old, **dict4_old}
assert merged_old == {"a": 1, "b": 2, "c": 3, "d": 4}
# With Python 3.9+, we can use the | operator for union
# This creates a new dictionary without modifying the originals
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = dict1 | dict2
assert merged == {"a": 1, "b": 2, "c": 3, "d": 4}
# The original dictionaries remain unchanged
assert dict1 == {"a": 1, "b": 2}
assert dict2 == {"c": 3, "d": 4}
# When there are overlapping keys, the right operand's values take precedence
# This is the same behavior as dict.update() and {**d1, **d2}
dict3 = {"a": 1, "b": 2, "c": 3}
dict4 = {"b": 20, "c": 30, "d": 4}
merged2 = dict3 | dict4
# Keys 'b' and 'c' from dict4 override those from dict3
assert merged2 == {"a": 1, "b": 20, "c": 30, "d": 4}
# The order matters! Left operand is the base, right operand overwrites
merged3 = dict4 | dict3
# Now keys 'b' and 'c' from dict3 override those from dict4
assert merged3 == {"b": 2, "c": 3, "d": 4, "a": 1}
# The |= operator performs in-place union (augmented assignment)
# This is equivalent to dict.update() but with cleaner syntax
dict5 = {"a": 1, "b": 2}
dict6 = {"c": 3, "d": 4}
dict5 |= dict6
# dict5 is modified in place
assert dict5 == {"a": 1, "b": 2, "c": 3, "d": 4}
# dict6 remains unchanged
assert dict6 == {"c": 3, "d": 4}
# The |= operator also handles overlapping keys
dict7 = {"a": 1, "b": 2, "c": 3}
dict8 = {"b": 20, "d": 4}
dict7 |= dict8
# 'b' from dict8 overwrites 'b' in dict7
assert dict7 == {"a": 1, "b": 20, "c": 3, "d": 4}
# You can chain multiple | operations
dict9 = {"a": 1}
dict10 = {"b": 2}
dict11 = {"c": 3}
dict12 = {"d": 4}
combined = dict9 | dict10 | dict11 | dict12
assert combined == {"a": 1, "b": 2, "c": 3, "d": 4}
# When chaining with overlapping keys, rightmost values win
dict13 = {"a": 1, "x": 10}
dict14 = {"b": 2, "x": 20}
dict15 = {"c": 3, "x": 30}
combined2 = dict13 | dict14 | dict15
# 'x' ends up with value 30 from the rightmost dictionary
assert combined2 == {"a": 1, "b": 2, "c": 3, "x": 30}
# The union operator works with empty dictionaries
empty: dict[str, int] = {}
dict16 = {"a": 1, "b": 2}
assert empty | dict16 == {"a": 1, "b": 2}
assert dict16 | empty == {"a": 1, "b": 2}
assert empty | empty == {}
# The union operator can be used with dict() constructor results
dict17 = dict(a=1, b=2)
dict18 = dict(c=3, d=4)
merged4 = dict17 | dict18
assert merged4 == {"a": 1, "b": 2, "c": 3, "d": 4}
# You can mix different value types in merged dictionaries
dict19 = {"name": "Alice", "age": 30}
dict20 = {"city": "NYC", "scores": [85, 90, 95]}
dict21 = {"active": True}
person = dict19 | dict20 | dict21
assert person == {"name": "Alice", "age": 30, "city": "NYC", "scores": [85, 90, 95], "active": True}
# Practical use case: Configuration merging
# Start with default configuration
default_config = {"timeout": 30, "retries": 3, "debug": False, "log_level": "INFO"}
# User provides custom configuration (partial)
user_config = {"timeout": 60, "debug": True}
# Merge configurations, user settings override defaults
final_config = default_config | user_config
assert final_config == {
"timeout": 60, # Overridden by user
"retries": 3, # From default
"debug": True, # Overridden by user
"log_level": "INFO", # From default
}
# Practical use case: Building objects incrementally
# Start with base attributes
base = {"id": 1, "type": "user"}
# Add authentication info
with_auth = base | {"username": "john", "email": "john@example.com"}
# Add profile info
with_profile = with_auth | {"bio": "Developer", "location": "USA"}
assert with_profile == {"id": 1, "type": "user", "username": "john", "email": "john@example.com", "bio": "Developer", "location": "USA"}
# Practical use case: Updating records with |=
user_record = {"id": 100, "name": "Jane", "status": "active", "login_count": 5}
# Apply update from an external source
update = {"status": "inactive", "login_count": 6, "last_login": "2024-01-15"}
user_record |= update
assert user_record == {"id": 100, "name": "Jane", "status": "inactive", "login_count": 6, "last_login": "2024-01-15"}
# The union operators only work with dictionaries
# Attempting to use them with non-dict types raises TypeError
dict22 = {"a": 1}
error_raised = False
try:
# This will fail because list is not a dict
dict22 | [("b", 2)] # type: ignore [operator]
except TypeError:
error_raised = True
assert error_raised is True
# However, you can use dict() to convert compatible types first
dict23 = {"a": 1}
dict24 = dict([("b", 2), ("c", 3)]) # Convert list of tuples to dict
merged5 = dict23 | dict24
assert merged5 == {"a": 1, "b": 2, "c": 3}
# Comparison with the older approaches shows the clarity improvement:
# OLD: Using update() - modifies original, no expression result
old1 = {"a": 1}
old1.update({"b": 2})
assert old1 == {"a": 1, "b": 2}
# OLD: Using unpacking - verbose for multiple merges
old2 = {**{"a": 1}, **{"b": 2}, **{"c": 3}}
assert old2 == {"a": 1, "b": 2, "c": 3}
# NEW: Using union operator - clean and chainable
new1 = {"a": 1} | {"b": 2} | {"c": 3}
assert new1 == {"a": 1, "b": 2, "c": 3}
if __name__ == "__main__":
main()