Key Takeaways: Dictionaries and Sets
One-Sentence Summary
Dictionaries map keys to values with O(1) average-case lookup, sets store unique elements with O(1) average-case membership testing, and choosing the right data structure is one of the most impactful decisions you'll make as a programmer.
Core Dictionary Operations
| Operation | Syntax | What It Does |
|---|---|---|
| Create | d = {"key": value} |
Create a dictionary with key-value pairs |
| Access | d["key"] |
Get value (raises KeyError if missing) |
| Safe access | d.get("key", default) |
Get value or return default if missing |
| Add/Update | d["key"] = value |
Set a key-value pair |
| Delete | del d["key"] |
Remove a key (raises KeyError if missing) |
| Delete (safe) | d.pop("key", default) |
Remove and return value, or return default |
| Membership | "key" in d |
Check if key exists (O(1)) |
| Length | len(d) |
Number of key-value pairs |
| Merge | d1 \| d2 |
Merge two dicts (Python 3.9+) |
Iteration Patterns
| What You Need | Method | Loop Pattern |
|---|---|---|
| Just keys | .keys() or default |
for key in d: |
| Just values | .values() |
for val in d.values(): |
| Both | .items() |
for key, val in d.items(): |
The Frequency Counter Pattern
This is the most important dictionary pattern — memorize it:
counts = {}
for item in sequence:
counts[item] = counts.get(item, 0) + 1
Or with Counter:
from collections import Counter
counts = Counter(sequence)
defaultdict(int) gives the same effect with plain += 1, and defaultdict(list) is the go-to for grouping items under a key.
Dictionary Comprehensions
{key_expr: value_expr for item in iterable if condition} builds a dict in one expression. For example, {name: s for name, s in scores.items() if s >= 70} keeps only passing scores. If the logic needs more than a line or two, a plain for loop reads better.
Set Operations at a Glance
| Operation | Operator | What It Returns |
|---|---|---|
| Union | a \| b |
Everything from both |
| Intersection | a & b |
Only what's in both |
| Difference | a - b |
In a but not in b |
| Symmetric difference | a ^ b |
In one but not both |
Choosing the Right Data Structure
| Question | Answer |
|---|---|
| "I need items in order, accessed by position" | list |
| "I need an immutable sequence" | tuple |
| "I need to look things up by a key/name" | dict |
| "I need to check membership fast or remove duplicates" | set |
| "I need to count things" | dict or Counter |
| "I need to group items by a category" | defaultdict(list) |
Threshold Concept: Hash-Based O(1) Lookup
- List search: O(n) — scan every element. 1 million items = up to 1 million checks.
- Dict/set lookup: O(1) on average — compute the hash, jump to that slot. 1 million items takes about the same few steps as 10 items.
- Keys must be hashable (immutable): strings, numbers, booleans, tuples whose contents are all hashable, frozensets.
- Mutable objects (lists, dicts, sets) cannot be keys — use tuples instead.
Common Gotchas
{}is an empty dict, not an empty set. Useset()for an empty set.inchecks keys, not values.3 in {"a": 3}isFalse.- Don't add or remove keys while looping over a dict (
RuntimeError). Loop overlist(d)instead, or build a new dict with a comprehension. - Keys are case-sensitive.
d["Name"]andd["name"]are different keys. - Sets have no reliable order. Converting a list to a set scrambles it; use
list(dict.fromkeys(items))to deduplicate while preserving order, andsorted(s)when you need to display a set predictably.
What's Next
Chapter 10: File I/O — save your dictionaries to JSON files, read data from CSV, and make your programs persistent.