Dictionaries are Python's most versatile data structure. Here is how to use them well.

Basic Operations

# Create
d = {"name": "Owen", "age": 25}
d = dict(name="Owen", age=25)
 
# Access
d["name"]       # "Owen"
d.get("name")   # "Owen"
d.get("missing", "default")  # "default"
 
# Add or update
d["email"] = "owen@example.com"
d.update({"city": "NYC", "age": 26})
 
# Delete
del d["age"]
value = d.pop("email")  # Returns and removes
d.clear()  # Remove all

Safe Access with get

user = {"name": "Owen"}
 
# Risky - raises KeyError if missing
user["email"]
 
# Safe - returns None or default
user.get("email")
user.get("email", "no email")

Always use get when the key might not exist.

setdefault

Get value if exists, set and return default if not:

counts = {}
 
# Instead of this
if "apple" not in counts:
    counts["apple"] = 0
counts["apple"] += 1
 
# Use setdefault
counts.setdefault("apple", 0)
counts["apple"] += 1
 
# Or for lists
groups = {}
groups.setdefault("fruit", []).append("apple")

Iteration

d = {"a": 1, "b": 2, "c": 3}
 
# Keys
for key in d:
    print(key)
 
for key in d.keys():
    print(key)
 
# Values
for value in d.values():
    print(value)
 
# Both
for key, value in d.items():
    print(key, value)

Dictionary Comprehensions

# Basic
squares = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
 
# With condition
evens = {x: x**2 for x in range(10) if x % 2 == 0}
 
# From two lists
keys = ["a", "b", "c"]
values = [1, 2, 3]
d = {k: v for k, v in zip(keys, values)}
 
# Swap keys and values
original = {"a": 1, "b": 2}
swapped = {v: k for k, v in original.items()}

Merging Dictionaries

d1 = {"a": 1, "b": 2}
d2 = {"b": 3, "c": 4}
 
# Python 3.9+
merged = d1 | d2  # {"a": 1, "b": 3, "c": 4}
 
# Update in place
d1 |= d2
 
# Earlier versions
merged = {**d1, **d2}

Later values override earlier ones.

Sorting

d = {"banana": 3, "apple": 1, "cherry": 2}
 
# By keys
sorted(d.items())
# [("apple", 1), ("banana", 3), ("cherry", 2)]
 
# By values
sorted(d.items(), key=lambda x: x[1])
# [("apple", 1), ("cherry", 2), ("banana", 3)]
 
# Create sorted dict
dict(sorted(d.items(), key=lambda x: x[1]))

Check Existence

d = {"a": 1, "b": 2}
 
# Check key
"a" in d       # True
"c" in d       # False
 
# Check value
1 in d.values()  # True

Default Values with defaultdict

from collections import defaultdict
 
# Instead of setdefault everywhere
counts = defaultdict(int)
counts["apple"] += 1  # No KeyError
 
groups = defaultdict(list)
groups["fruit"].append("apple")

Nested Dictionaries

users = {
    "user1": {"name": "Owen", "age": 25},
    "user2": {"name": "Alex", "age": 30}
}
 
# Access nested value
users["user1"]["name"]
 
# Safe nested access
def get_nested(d, *keys, default=None):
    for key in keys:
        if isinstance(d, dict):
            d = d.get(key, default)
        else:
            return default
    return d
 
get_nested(users, "user1", "name")  # "Owen"
get_nested(users, "user3", "name")  # None

Common Patterns

Count occurrences

from collections import Counter
 
words = ["apple", "banana", "apple", "cherry"]
counts = Counter(words)
# Counter({"apple": 2, "banana": 1, "cherry": 1})

Group by key

from collections import defaultdict
 
items = [
    {"type": "fruit", "name": "apple"},
    {"type": "vegetable", "name": "carrot"},
    {"type": "fruit", "name": "banana"},
]
 
grouped = defaultdict(list)
for item in items:
    grouped[item["type"]].append(item["name"])

Invert dictionary

original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
# {1: "a", 2: "b", 3: "c"}

Filter dictionary

d = {"a": 1, "b": 2, "c": 3, "d": 4}
filtered = {k: v for k, v in d.items() if v > 2}
# {"c": 3, "d": 4}

Performance

  • Access by key: O(1)
  • Check existence: O(1)
  • Iteration: O(n)

Dictionaries are hash tables. Key lookup is fast regardless of size.

Dictionaries are essential. Learn these patterns and you will write cleaner Python.

React to this post: