The dict.update([other, **kwargs]) method updates an existing dictionary in-place by adding new key-value pairs and overwriting existing keys with incoming values, returning None.
Flexible Ingestion Formats: update() accepts another dictionary, an iterable yielding (key, value) 2-tuples (such as zip() streams), or arbitrary keyword arguments (k=v), providing a unified API for batch dictionary mutations.
The standard CPython signature for dict.update() is:
dictionary.update([other, **kwargs])
| Parameter | Type | Default | Behavior & Description |
|---|---|---|---|
other |
dict or Iterable[Tuple[K, V]] |
Optional | Another dictionary or an iterable of key-value pairs (e.g., list of 2-tuples). |
**kwargs |
Keyword arguments | Optional | Key-value pairs passed as keyword arguments (e.g., key=value). Keys must be valid Python identifiers. |
| Return Value | None |
— | Mutates the dictionary in-place and always returns None. |
Python's update() method accepts three distinct input formats to accommodate different data sources:
base_config = {"host": "localhost", "port": 8080}
# Format 1: Another dictionary
base_config.update({"port": 9000, "ssl": True})
print("Format 1 (dict): ", base_config)
# Format 2: Iterable of key-value 2-tuples (e.g. from zip)
new_pairs = [("timeout", 30), ("retries", 5)]
base_config.update(new_pairs)
print("Format 2 (tuples): ", base_config)
# Format 3: Direct keyword arguments (**kwargs)
base_config.update(debug=False, workers=4)
print("Format 3 (**kwargs):", base_config)
Introduced in Python 3.9 (PEP 584), dictionaries natively support the pipe union operators. Understand when to mutate in-place versus returning a new dictionary:
| Syntax | In-Place Mutation? | Returns New Object? | Operands Accepted | Typical Use Case |
|---|---|---|---|---|
d.update(other) |
Yes | No (returns None) |
Dict, tuple iterables, kwargs | Mutating existing state, accumulator loops, polymorphic inputs. |
d |= other |
Yes | No (in-place union) | Dict, tuple iterables | Concise, readable in-place dictionary updates in modern Python. |
d3 = d1 | d2 |
No (Preserves d1 & d2) | Yes (returns new dict) | Dictionaries only | Pure functional merging where original inputs must remain immutable. |
d1 = {"a": 1, "b": 2}
d2 = {"b": 20, "c": 30}
# Functional merge (|): d1 and d2 remain completely unchanged
merged_new = d1 | d2
print("Merged new dict:", merged_new) # {'a': 1, 'b': 20, 'c': 30}
print("d1 untouched: ", d1) # {'a': 1, 'b': 2}
# In-place update (|= or .update()): d1 is mutated directly
d1 |= d2
print("d1 mutated: ", d1) # {'a': 1, 'b': 20, 'c': 30}
A vital architectural rule: update() performs a shallow replacement at the top-level keys. If a key maps to an inner sub-dictionary, update() completely overwrites the inner dictionary instead of merging its inner keys:
server = {
"database": {"host": "localhost", "port": 5432},
"env": "dev"
}
# Overriding only the database port
patch = {"database": {"port": 5433}}
server.update(patch)
# TRAP: 'host' was wiped out because 'database' was replaced entirely!
print("Resulting server:", server["database"])
# Output: {'port': 5433} ('host' is gone!)
When you need to preserve existing sub-dictionary keys while updating nested options, implement a recursive merge:
def deep_merge_dicts(base: dict, overrides: dict) -> dict:
for k, v in overrides.items():
if k in base and isinstance(base[k], dict) and isinstance(v, dict):
deep_merge_dicts(base[k], v)
else:
base[k] = v
return base
config_base = {"db": {"host": "localhost", "port": 5432}, "env": "dev"}
config_patch = {"db": {"port": 5433}, "env": "prod"}
merged = deep_merge_dicts(config_base, config_patch)
print("Deep merged DB:", merged["db"]) # {'host': 'localhost', 'port': 5433} (both preserved!)
Ingest paired sequences directly into a dictionary using update() with zip():
headers = ["id", "username", "role"]
row_values = [401, "sara_dev", "moderator"]
user_dict = {}
user_dict.update(zip(headers, row_values))
print("Zipped into dictionary:", user_dict)
Construct layered application configurations (System Defaults → User Profile → Runtime CLI flags):
app_config = {"theme": "light", "fontSize": 14, "autoSave": True}
user_prefs = {"theme": "dark", "fontSize": 16}
cli_flags = {"autoSave": False}
# Apply layers in priority order
app_config.update(user_prefs)
app_config.update(cli_flags)
print("Resolved config:", app_config)
Like other in-place mutators, d.update() returns None. Writing d = d.update(...) will assign None to d and permanently erase your dictionary.
When using the d.update(key=val) keyword format, keys must be valid Python identifiers (no spaces, dashes, or leading numbers). For arbitrary string keys like "user-id" or 101, pass a dictionary mapping instead: d.update({"user-id": 101}).
Run this complete test harness verifying multi-format updates, merge operator parity, and recursive merging:
class UpdateVerifier:
@staticmethod
def merge_three_layers(base: dict, mid: dict, top: dict) -> dict:
res = dict(base)
res.update(mid)
res.update(top)
return res
# Verification unit assertions
v = UpdateVerifier()
# Test 1: Multi-layer update priority
resolved = v.merge_three_layers({"a": 1, "b": 2}, {"b": 20, "c": 30}, {"c": 300})
assert resolved == {"a": 1, "b": 20, "c": 300}
# Test 2: Tuple sequence format
d_tuples = {"x": 10}
d_tuples.update([("y", 20), ("z", 30)])
assert d_tuples == {"x": 10, "y": 20, "z": 30}
# Test 3: kwargs format
d_kwargs = {"p": 1}
d_kwargs.update(q=2, r=3)
assert d_kwargs == {"p": 1, "q": 2, "r": 3}
print("All dict.update() unit assertions passed successfully!")
Open in Google Colab View on GitHub
Run all dictionary update exercises interactively, test input formats, contrast in-place update with the union operator (|), and build recursive deep mergers in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.