Dictionaries
A dictionary (dict) maps keys to values — like a real menu maps a
dish to its price. When you need to look something up by name rather than by
position, a dict is the tool. This lesson builds the app's menu and totals an
order from it.
Every box is live — edit and ▶ Run.
Part 1 — Key → value
A dict uses curly braces with key: value pairs. Look a value up by its key with
square brackets:
Keys are usually strings (like here), but can be numbers too. Each key is unique — and "unique" has a consequence people don't expect:
menu = {"soup": 10, "steak": 20, "soup": 12}
print(menu["soup"])❓ Cross-question — "Is a Python dict a JS object or a
Map?" Closer to aMap: keys can be any hashable value (not only strings), insertion order is preserved, and you access withd["soup"], neverd.soup(dot-access is for object attributes, not dict keys). For typing,dict[str, float]≈ TSRecord<string, number>.
Watch out: looking up a missing key with
[]crashes. Run this to see theKeyError:
Part 2 — .get(): safe lookups with a default
menu.get(key, default) returns the value if the key exists, or the default
if it doesn't — no crash. This is how you handle "dish not on the menu":
You'll reach for .get(..., 0) constantly when adding up numbers from a dict.
❓ Cross-question — "Does
menu["pizza"]returnundefinedlike a JS object?" No — it raisesKeyErrorand stops the program. That's exactly why.get(key, default)exists — the safe lookup (≈ JSobj[key] ?? fallback). Use[]only when a missing key genuinely is a bug you want to hear about loudly.
Part 3 — Add, update, and check
Dicts are mutable — assign to a key to add or overwrite it, and use in to
check for a key:
Part 4 — Looping over a dict
Looping a dict directly gives you its keys. Use .values() for the values and
.items() for both at once:
❓ Cross-question — "What's the
Object.entries/Map.entriesequivalent?" It's.items()—for k, v in d.items()≈for (const [k, v] of Object.entries(obj)). And plainfor k in dgives keys (≈Object.keys),.values()gives values. NoObject.keys()wrapper needed to iterate.
Part 5 — Total an order from the menu
Now put it together: walk the order, look each dish up with .get(dish, 0), and
add it to a running total (the accumulator pattern from Lesson 5):
Part 6 — Lists vs Tuples vs Sets vs Dictionaries
You've now met all four of Python's built-in collections. They look alike but each is built for a different job. Here's the whole picture on one screen:
| Type | Written as | Ordered? | Changeable? | Duplicates? | Access by |
|---|---|---|---|---|---|
| List | ["soup", "steak"] |
Yes | Yes | Allowed | position — dishes[0] |
| Tuple | ("soup", "steak") |
Yes | No (frozen) | Allowed | position — point[0] |
| Set | {"soup", "steak"} |
No | Yes | No (unique) | membership — x in s |
| Dict | {"soup": 10} |
Yes | Yes | Keys unique | key — menu["soup"] |
Dicts also remember the order you inserted the keys (Python 3.7+).
Same dishes, four different tools — run this and watch how each one behaves:
The { } trap
Sets and dicts both use curly braces, so an empty {} is a dict, not a
set — for an empty set you must write set():
Which one do I reach for?
- An ordered list you'll add to or change → list
- A fixed group that must never change (a coordinate, a row) → tuple
- Only the unique values, or fast "is it in here?" checks → set
- Look something up by name (dish → price, user → email) → dict
Part 7 — Idioms & real-world patterns
Dicts are the workhorse of real Python — config, JSON, counting, grouping. These are the moves you'll use daily.
Dict comprehensions & dict(zip(...))
Build a dict in one expression:
.setdefault and defaultdict — accumulate into a dict
Grouping items by a key is a daily task. .setdefault(key, []) gets-or-creates
the bucket; collections.defaultdict does it automatically:
Coming from Java:
by_table.setdefault(k, []).append(v)ismap.computeIfAbsent(k, x -> new ArrayList<>()).add(v).
Counter — count things in one line
Merging dicts — defaults + overrides
Coming from JS:
{**defaults, **user}is spread merge{...defaults, ...user}— later keys win, exactly the same.
Copying a dict — = does not do it
Exactly the trap you met with lists in lesson 11, and it bites just as often here.
= gives a second name for one dict, not a second dict:
Step it and watch menu change on the alias[...] = 99 line, then not change
when real is edited. dict(menu) and {**menu} do the same job as .copy().
All three are shallow — the outer dict is new, but nested values are still
shared. If a value is itself a list, mutating it through either name shows up in
both, and you want copy.deepcopy() instead.
Essentials: .update / .pop / del, and inverting
Reminder (hashability): dict keys must be immutable/hashable —
str,int,tupleare fine; alistkey raisesTypeError(see Lesson 11).
🎯 Your turn
Write order_total(order, menu) — it totals the prices of the ordered dishes,
treating any dish not on the menu as 0:
order_total(["soup", "steak"], {"soup": 10, "steak": 20})→30order_total(["soup", "cake"], {"soup": 10})→10
Hint — start total = 0, loop the order, and add menu.get(dish, 0) each
time. Return total.
Then press ▶ Run, tap the Live App try chips, and hit ✓ Check. Green = this piece of the app is built. ✅
