Dictionaries

May 18, 2026
#python #dictionaries #data-structures #beginner

Dictionaries are Python’s built-in hash map — they store key-value pairs and give you O(1) lookups by key. If lists are for ordered sequences, dictionaries are for labeled data: user profiles, configuration, API responses, anything where you access values by name rather than position.

Creating Dictionaries

# Empty dict
empty = {}

# Dict with values
user = {
    "name": "Alice",
    "email": "alice@example.com",
    "age": 28,
    "active": True
}

# From a list of tuples
pairs = [("a", 1), ("b", 2), ("c", 3)]
d = dict(pairs)  # {'a': 1, 'b': 2, 'c': 3}

# From keyword arguments
config = dict(host="localhost", port=5432, debug=True)

Accessing Values

user = {"name": "Alice", "email": "alice@example.com", "age": 28}

# Bracket notation (raises KeyError if key missing)
print(user["name"])   # Alice

# .get() (returns None or a default if key missing)
print(user.get("phone"))          # None
print(user.get("phone", "N/A"))   # N/A

Use .get() when the key might not exist. Use brackets when you’re certain it does (or want an error if it doesn’t).

Adding, Updating, and Deleting

user = {"name": "Alice"}

# Add or update
user["email"] = "alice@example.com"
user["name"] = "Alice Smith"  # update existing

# Update multiple keys at once
user.update({"age": 28, "role": "admin"})

# Delete
del user["role"]                    # raises KeyError if missing
removed = user.pop("age")           # removes and returns value (28)
removed = user.pop("phone", None)   # returns None if missing (no error)

# Remove last inserted item
last_key, last_value = user.popitem()

Checking Keys

user = {"name": "Alice", "email": "alice@example.com"}

# `in` checks keys (not values)
print("name" in user)     # True
print("phone" in user)    # False
print("Alice" in user)    # False (checks keys, not values)

Iterating

scores = {"math": 95, "english": 88, "science": 92}

# Keys (default)
for subject in scores:
    print(subject)

# Values
for score in scores.values():
    print(score)

# Both
for subject, score in scores.items():
    print(f"{subject}: {score}")

Dictionary Comprehensions

Like list comprehensions, but for dicts:

# Basic
squares = {x: x**2 for x in range(6)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# With filtering
words = ["hello", "world", "python", "code"]
lengths = {w: len(w) for w in words if len(w) > 4}
# {'hello': 5, 'world': 5, 'python': 6}

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

Nested Dictionaries

users = {
    "alice": {
        "email": "alice@example.com",
        "preferences": {"theme": "dark", "language": "en"}
    },
    "bob": {
        "email": "bob@example.com",
        "preferences": {"theme": "light", "language": "fr"}
    }
}

# Access nested values
print(users["alice"]["preferences"]["theme"])  # dark

# Safe nested access
theme = users.get("charlie", {}).get("preferences", {}).get("theme", "light")
print(theme)  # light (default, since charlie doesn't exist)

Merging Dictionaries

defaults = {"theme": "light", "font_size": 14, "language": "en"}
user_prefs = {"theme": "dark", "font_size": 16}

# Spread operator (Python 3.9+)
merged = {**defaults, **user_prefs}
# {'theme': 'dark', 'font_size': 16, 'language': 'en'}

# Union operator (Python 3.9+)
merged = defaults | user_prefs

# In-place merge
defaults.update(user_prefs)

Common Patterns

Counting occurrences

text = "hello world hello python hello"
words = text.split()

# Manual counting
counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1
print(counts)  # {'hello': 3, 'world': 1, 'python': 1}

# Using Counter (preferred)
from collections import Counter
counts = Counter(words)
print(counts.most_common(2))  # [('hello', 3), ('world', 1)]

Grouping items

people = [
    {"name": "Alice", "dept": "Engineering"},
    {"name": "Bob", "dept": "Marketing"},
    {"name": "Charlie", "dept": "Engineering"},
    {"name": "Diana", "dept": "Marketing"},
]

from collections import defaultdict

by_dept = defaultdict(list)
for person in people:
    by_dept[person["dept"]].append(person["name"])

print(dict(by_dept))
# {'Engineering': ['Alice', 'Charlie'], 'Marketing': ['Bob', 'Diana']}

Building a lookup table

users_list = [
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
    {"id": 3, "name": "Charlie"},
]

# O(1) lookup by ID instead of O(n) list search
users_by_id = {u["id"]: u for u in users_list}
print(users_by_id[2]["name"])  # Bob

Caching / memoization

cache = {}

def expensive_calculation(n):
    if n in cache:
        return cache[n]

    # Simulate expensive work
    result = sum(range(n))
    cache[n] = result
    return result

setdefault

Initialize a key only if it doesn’t exist:

inventory = {}

# Without setdefault
if "apples" not in inventory:
    inventory["apples"] = []
inventory["apples"].append("Granny Smith")

# With setdefault (one line)
inventory.setdefault("oranges", []).append("Navel")

Ordering

Since Python 3.7, dictionaries maintain insertion order:

d = {"b": 2, "a": 1, "c": 3}
print(list(d.keys()))  # ['b', 'a', 'c'] — insertion order preserved

# Sort by key
sorted_d = dict(sorted(d.items()))
# {'a': 1, 'b': 2, 'c': 3}

# Sort by value
sorted_by_val = dict(sorted(d.items(), key=lambda item: item[1]))
# {'a': 1, 'b': 2, 'c': 3}

Performance

Operation Time Complexity
d[key] (get) O(1) average
d[key] = value (set) O(1) average
key in d O(1) average
del d[key] O(1) average
Iterating all items O(n)

Dictionaries are implemented as hash tables. Keys must be hashable (immutable): strings, numbers, tuples. Lists and other dicts cannot be keys.

What’s Next

Dictionaries and lists together handle most data manipulation in Python. From here, explore classes and OOP to create your own data types, or dive into file I/O to read and write data from disk.