Python Dictionaries
Dictionary creation, access patterns, comprehensions, and merging.
Creating Dicts
Dict literal — the standard way, keys must be hashable (strings, ints, tuples)
d = {"host": "10.50.1.20", "port": 443}
Dict constructor with kwargs — cleaner for string keys, no quotes needed
d = dict(host="10.50.1.20", port=443)
Dict from parallel lists — zip pairs them up, dict converts pairs to key-value
d = dict(zip(keys, values))
Initialize keys with same value — creates {"a": 0, "b": 0, "c": 0}, beware mutable defaults
d = dict.fromkeys(["a", "b", "c"], 0)
Accessing Values
Direct access — raises KeyError if key missing, use when key must exist
host = d["host"]
Safe access with default — returns "unknown" if key missing, never raises KeyError
host = d.get("host", "unknown")
Get-or-initialize — if key missing, sets it to default and returns it; atomic for mutable defaults
d.setdefault("sessions", []).append(session)
Modifying Dicts
Merge dict into existing — overwrites existing keys, adds new ones, mutates in place
d.update({"port": 8443, "proto": "https"})
Merge operator (Python 3.9+) — returns new dict, d2 values win on conflict
merged = d1 | d2
In-place merge (Python 3.9+) — mutates d1, equivalent to d1.update(d2)
d1 |= d2
Spread merge with extras — unpacks both dicts and adds literal keys, d2 wins conflicts
merged = {**d1, **d2, "extra": "value"}
Remove and return — deletes key, returns value; default prevents KeyError if missing
old_val = d.pop("temp_key", None)
Delete key — raises KeyError if missing, use pop with default for safe deletion
del d["host"]
Dict Comprehensions
Filter dict — remove None values, build new dict from filtered items
clean = {k: v for k, v in d.items() if v is not None}
Transform dict values — apply function to every value while keeping keys
upper = {k: v.upper() for k, v in d.items()}
Invert dict — swap keys and values, only works if values are unique and hashable
inverted = {v: k for k, v in d.items()}
Iteration and Membership
Iterate key-value pairs — .items() returns view of (key, value) tuples
for key, value in d.items():
print(f"{key} = {value}")
Extract keys or values as list — views are live, wrap in list() if you need a snapshot
all_keys = list(d.keys())
all_vals = list(d.values())
Key membership test — O(1) hash lookup, do not use "host" in d.keys()
if "host" in d:
connect(d["host"])
Sort by value descending — returns list of tuples, use dict() to re-wrap if needed
ranked = sorted(d.items(), key=lambda x: x[1], reverse=True)
Specialized Dict Types
defaultdict — auto-initializing, dd["new_key"].append(x) works without checking key existence
from collections import defaultdict
by_vlan = defaultdict(list)
for device in devices:
by_vlan[device.vlan].append(device.hostname)
Counter — frequency counting, c.most_common(5) returns top 5, supports arithmetic between Counters
from collections import Counter
c = Counter(log_levels)
c.most_common(5) # [("ERROR", 42), ("WARN", 17), ...]
c["ERROR"] # returns 0 for missing keys, not KeyError
Nested Dicts
Nested dict — access with chained keys, chain carefully
config = {
"ise": {
"host": "10.50.1.20",
"ers": {"port": 9060}
}
}
port = config["ise"]["ers"]["port"]
Safe nested access — walks a key path without risking KeyError at any depth
from functools import reduce
def deep_get(d, keys, default=None):
return reduce(lambda obj, key: obj.get(key, {}) if isinstance(obj, dict) else default, keys, d)
port = deep_get(config, ["ise", "ers", "port"])
Serialization
Serialize dict to JSON — default=str handles datetime and other non-serializable types
import json
print(json.dumps(d, indent=2, default=str))