Home› Tutorials› Data Structures
Data StructuresDictionary Techniques in Python: Beyond Basic Key-Value Storage
- Dict comprehensions build dicts from any iterable in a single expression.
dict.get(key, default)avoidsKeyErrorfor optional keys.defaultdictfromcollectionsinitialises missing keys automatically.- The
|operator (Python 3.9+) merges two dicts into a new one without mutation.
Creation methods
A dict can be created in several equivalent ways. Each has a context where it reads more naturally:
# Literal syntax
config = {"host": "localhost", "port": 5432, "ssl": True}
# dict() constructor with keyword arguments
config = dict(host="localhost", port=5432, ssl=True)
# From a list of key-value tuples (common when transforming data)
pairs = [("a", 1), ("b", 2), ("c", 3)]
mapping = dict(pairs)
# From two parallel sequences
keys = ["x", "y", "z"]
vals = [10, 20, 30]
combined = dict(zip(keys, vals))
Dict comprehensions
Like list comprehensions but with a key-value pair separated by a colon:
words = ["apple", "banana", "cherry"]
lengths = {word: len(word) for word in words}
# {"apple": 5, "banana": 6, "cherry": 6}
# Invert a mapping (assumes values are unique)
original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
# {1: "a", 2: "b", 3: "c"}
# Filter while building
scores = {"Alice": 92, "Bob": 55, "Carol": 78}
passing = {name: score for name, score in scores.items() if score >= 60}
# {"Alice": 92, "Carol": 78}
Safe access with get() and setdefault()
Accessing a missing key raises KeyError. Two methods let you handle this gracefully:
data = {"name": "Alice", "role": "admin"}
# get() returns None (or a default) for missing keys
email = data.get("email") # None
email = data.get("email", "not set") # "not set"
# setdefault() inserts and returns the default if missing
visits = {}
for page in access_log:
visits.setdefault(page, 0)
visits[page] += 1
setdefault is particularly useful for accumulating values into a list under a shared key without checking for existence first:
grouped = {}
for item in items:
grouped.setdefault(item["category"], []).append(item)
defaultdict for automatic initialisation
collections.defaultdict takes a callable. When a missing key is accessed, it calls that callable to produce the initial value:
from collections import defaultdict
word_positions = defaultdict(list)
for i, word in enumerate(text.split()):
word_positions[word].append(i)
# No KeyError, no setdefault needed
counter = defaultdict(int)
for event in events:
counter[event["type"]] += 1
The first argument can be any zero-argument callable: list, int, set, or a lambda for more complex defaults like lambda: {"count": 0, "total": 0.0}.
Merging dicts
Python 3.9 introduced the | operator for merging. When keys overlap, the right-hand dict wins:
defaults = {"color": "blue", "size": "medium", "border": True}
overrides = {"color": "red", "border": False}
merged = defaults | overrides
# {"color": "red", "size": "medium", "border": False}
# In-place merge (updates defaults in-place)
defaults |= overrides
For Python 3.8 and earlier, use unpacking: {**defaults, **overrides}. The effect is identical but less readable.
Sorting a dict by value or key
Dicts preserve insertion order in Python 3.7+, but sorted() returns a new sorted structure:
scores = {"Alice": 92, "Bob": 55, "Carol": 78, "Dave": 88}
# Sort by value descending; result is a list of tuples
ranked = sorted(scores.items(), key=lambda kv: kv[1], reverse=True)
# [("Alice", 92), ("Dave", 88), ("Carol", 78), ("Bob", 55)]
# Rebuild as a sorted dict
ranked_dict = dict(ranked)
Counter for frequency analysis
collections.Counter is a dict subclass built for tallying. It accepts any iterable and counts occurrences:
from collections import Counter
words = "the quick brown fox jumps over the lazy dog the".split()
freq = Counter(words)
print(freq.most_common(3))
# [("the", 3), ("quick", 1), ("brown", 1)]
# Counter supports arithmetic
a = Counter(["x", "y", "x"])
b = Counter(["y", "y", "z"])
print(a + b) # Counter({"y": 3, "x": 2, "z": 1})
print(a - b) # Counter({"x": 2}) (drops non-positive counts)
dict maintains insertion order as part of the language specification, not just as an implementation detail. You can rely on for k, v in d.items() yielding items in the order they were inserted.