Chapter 7

Dictionaries

7.1 Dictionaries · 7.2 Core Operations · 7.3 Dictionary Patterns · 7.4 Sets

← → or Space to navigate · F for fullscreen

7.1 Dictionaries

Key-value mappings — the workhorse of Python data

What Is a Dictionary?

A dictionary stores data as key–value pairs.

  • Look up a key → get its value
  • Keys must be unique and hashable
  • Values can be any type
person = {"name": "Alice", "age": 28}
config = {"debug": True, "timeout": 30}
word_count = {}          # empty dict

When to use a dict

  • You need labeled data (vs positional)
  • You want fast lookups by name
  • You need a mapping relationship
Use case Example
Student records {"id": 42, "gpa": 3.8}
Config settings {"theme": "dark"}
Word counts {"the": 12, "cat": 3}
JSON data any API response

Creating Dictionaries

Dict literal

scores = {"alice": 92, "bob": 85}

dict() constructor

# from keyword arguments
d = dict(name="Alice", age=28)

# from list of (key, value) pairs
pairs = [("a", 1), ("b", 2)]
d = dict(pairs)

dict.fromkeys()

# all keys share the same default value
keys = ["x", "y", "z"]
d = dict.fromkeys(keys, 0)
# {"x": 0, "y": 0, "z": 0}

Dict comprehension

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

Keys must be hashable — str, int, float, and tuple work; list and dict do not.

Nesting

students = {
    "alice": {"gpa": 3.9, "year": 2},
    "bob":   {"gpa": 3.4, "year": 3},
}
print(students["alice"]["gpa"])   # 3.9

7.2 Core Operations

Accessing, modifying, and querying dictionaries

Accessing Items

Square-bracket access

person = {"name": "Alice", "age": 28}

print(person["name"])      # Alice
# person["email"]          # KeyError!

Safe access with .get()

# returns None if key missing
print(person.get("email"))        # None

# returns default if key missing
print(person.get("email", "N/A")) # N/A

Membership testing

"name" in person      # True
"email" in person     # False
"email" not in person # True

Prefer .get(key, default) over dict[key] when the key might be absent — it avoids a KeyError without needing a try/except.

Lists and Dictionaries: Access and Mutation

Lists use positions; dictionaries use keys.

Operation List nums Dictionary d
Read nums[i] d[key]
Read with fallback No get() equivalent d.get(key, default)
Replace nums[i] = value (existing index) d[key] = value (add or replace)
Add nums.append(value) or nums.insert(i, value) d[key] = value (new key)
Delete, no return del nums[i] or del nums[i:j] del d[key]

Invalid indices raise IndexError; missing keys raise KeyError when no fallback is supplied.

Lists and Dictionaries: Removal and Inspection

Operation List nums Dictionary d
Remove by value nums.remove(value) removes first match No remove(); deletion uses keys
Remove and return nums.pop(i); no fallback d.pop(key, default)
Remove last nums.pop() returns an element d.popitem() returns (key, value)
Clear nums.clear() d.clear()
Membership value in nums key in d; value in d.values()
Iterate for value in nums for key in d; for key, value in d.items()
Count len(nums) len(d)

Adding, Updating, and Deleting

Add / update

person["email"] = "alice@example.com"  # add
person["age"] = 29                     # update

# update multiple keys at once
person.update({"age": 30, "city": "Rolla"})

Delete

del person["city"]           # statement, no return
email = person.pop("email")  # remove, return value
phone = person.pop("phone", "N/A")  # default if missing
last = person.popitem()      # remove last inserted pair

del also removes list elements, slices, and variable bindings. It does not return a value.

Dictionary pop(key, default) uses a key and an optional fallback. List pop(index) uses an optional index and has no fallback. Without a fallback, a missing dictionary key raises KeyError. Like list pop(), d.popitem() removes the last item, but returns a (key, value) tuple.

Useful inspection methods

person.keys()    # dict_keys([...])
person.values()  # dict_values([...])
person.items()   # dict_items([(k,v), ...])
len(person)      # number of key-value pairs

dict.keys(), .values(), and .items() return view objects — they reflect changes to the dict in real time.

7.3 Dictionary Patterns

Comprehensions, counting, defaults, iteration, and memory

Dictionary Comprehension

Same idea as list comprehension — builds a dict in one expression.

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

# with filter: only even squares
even_sq = {x: x**2 for x in range(6)
           if x % 2 == 0}
# {0:0, 2:4, 4:16}

# transform an existing dict
prices = {"apple": 1.2, "banana": 0.5}
discounted = {k: v * 0.9
              for k, v in prices.items()}

Invert a dictionary

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

Inversion only works cleanly when values are unique. Duplicate values will overwrite each other.

Counting Patterns

Manual counter

words = "the cat sat on the mat".split()

counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1
# {"the":2, "cat":1, "sat":1, ...}

Counter — one line

from collections import Counter

counts = Counter(words)
counts.most_common(3)
# [("the", 2), ("cat", 1), ("sat", 1)]

# combine two counters
c2 = Counter(["the", "dog"])
print(counts + c2)

defaultdict — skip the guard

from collections import defaultdict

# int factory: missing keys default to 0
word_count = defaultdict(int)
for word in words:
    word_count[word] += 1   # no KeyError

# list factory: group values by key
groups = defaultdict(list)
for name, score in records:
    groups[name].append(score)

Counter is best for tallying. defaultdict is best for grouping or accumulating into containers.

Iterating Through Dictionaries

d = {"name": "Alice", "age": 28, "city": "Rolla"}

Keys (default)

for key in d:
    print(key)
# name  age  city

Values

for val in d.values():
    print(val)
# Alice  28  Rolla

Key-value pairs

for key, val in d.items():
    print(f"{key}: {val}")
# name: Alice
# age: 28
# city: Rolla

Sorted iteration

for key in sorted(d):
    print(key, d[key])

Do not add or remove keys while iterating — it raises RuntimeError. Iterate over list(d.items()) if you need to modify during the loop.

Nested Dictionaries

A dictionary value can be another dictionary: a nested dictionary, often used for records and JSON data.

  • Chain keys, outer first: student["grades"]["math"].
  • student["grades"] is the inner dict, not a copy, so updates show through student.
  • Protect each level: .get() for a missing key, setdefault() to create a missing inner dict.
student = {
    "name": "Alice",
    "grades": {"math": 88, "science": 92},
}
print(student["grades"]["math"])   # 88

student["grades"]["math"] = 90
student["grades"].update({"history": 78})
print(student["grades"])
# {'math': 90, 'science': 92, 'history': 78}

print(student["grades"].get("art", "no grade"))
student.setdefault("contact", {})["city"] = "Austin"

Why Dict Lookup Is Fast — O(1)

  • Dicts and sets use hash tables.
  • Python computes hash(key) → jumps directly to the bucket.
  • Lookup is O(1) average regardless of dict size.
  • List in operator scans every element — O(n).
import time, random

n = 1_000_000
big_list = list(range(n))
big_dict = {i: True for i in range(n)}
target = n - 1   # worst case for list

t = time.perf_counter()
_ = target in big_list
print(f"list: {time.perf_counter()-t:.5f}s")

t = time.perf_counter()
_ = target in big_dict
print(f"dict: {time.perf_counter()-t:.5f}s")
Operation list dict / set
x in c O(n) O(1) avg
c[key] O(1) by index O(1) by key
Insert O(1) append O(1) avg
Delete O(n) O(1) avg

Hashable keys: int, str, float, tuple (of hashables) are hashable. list and dict are not — they cannot be dict keys.

7.4 Sets

Unordered collections of unique, hashable elements

Creating Sets and Membership

Create

a = {1, 2, 3}
b = set([3, 4, 5])    # from list
empty = set()          # NOT {} (that's a dict)

# duplicates removed automatically
s = {1, 2, 2, 3, 3}   # {1, 2, 3}

No indexing — iterate or test membership

3 in a      # True   O(1)
6 not in a  # True

for item in a:
    print(item)    # order not guaranteed

When to use a set

Need Use
Ordered, duplicates OK list
Fixed structure, hashable tuple
Unique elements, fast lookup set
Key-value mapping dict

Sets are great for deduplication and membership testing. x in set is O(1); x in list is O(n).

Set Algebra and Mutation

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
Operation Operator Method
Union a | b a.union(b)
Intersection a & b a.intersection(b)
Difference a - b a.difference(b)
Sym. difference a ^ b a.symmetric_difference(b)
a | b   # {1, 2, 3, 4, 5, 6}
a & b   # {3, 4}
a - b   # {1, 2}
a ^ b   # {1, 2, 5, 6}

Modify in place

a.add(5)          # add one element
a.update({6, 7})  # add multiple

a.remove(1)       # KeyError if missing
a.discard(99)     # safe — no error
a.pop()           # remove arbitrary element

Subset / superset

{1, 2} <= {1, 2, 3}   # True  (subset)
{1, 2, 3} >= {1, 2}   # True  (superset)

Frozensets, Hashability, and Performance

frozenset — immutable set

fs = frozenset({1, 2, 3})
# fs.add(4)   ← AttributeError

# Can be a dict key or set element
graph = {frozenset({1, 2}): "edge A"}
set frozenset
Mutable ✓ ✗
Hashable ✗ ✓
Dict key ✗ ✓

Set comprehension

# unique squares of even numbers
s = {x**2 for x in range(10) if x % 2 == 0}
# {0, 4, 16, 36, 64}

# deduplicate a list
words = ["the", "cat", "the", "mat"]
unique = {w.lower() for w in words}

O(1) membership — why it matters

# set membership is O(1) vs O(n) for list
big = set(range(1_000_000))
999_999 in big   # instant

Chapter 7 — Quick Reference

Concept Key syntax / notes
Create {"k": v} · dict(k=v) · dict.fromkeys(keys, 0)
Access d[key] (KeyError if missing) · d.get(key, default) (safe)
Add / update d[key] = val · d.update({...})
Delete del d[key] · d.pop(key) · d.popitem()
Membership key in d — O(1)
Iterate d.keys() · d.values() · d.items()
Comprehension {k: v for k, v in iterable}
Count Counter(seq) → .most_common(n)
Group defaultdict(list) — no KeyError on first access
Invert {v: k for k, v in d.items()}
Memoize Store previous results in a dict to avoid repeated work
Sets {1, 2} · set() for an empty set · s.add(x) · s.discard(x)
Set algebra a | b · a & b · a - b · a ^ b
Deduplicate list(set(lst))

End of Chapter 7

Next: Chapter 8 — Strings

string methods · regex · text analysis · word frequencies