Looking Things Up Instead of Counting Along
A list makes you remember where something is. A dictionary lets you forget. Build one from scratch, look something up by name instead of position, and ask for a key that was never there.
A list makes you remember where something is. A dictionary lets you forget. Build one from scratch, look something up by name instead of position, and ask for a key that was never there.
Looking up by name instead of position
A list of prices tells you nothing unless you already remember that index 0 is apples. A dictionary stores the name alongside the value, and you look up by that name directly.
$ >>> prices = {"apple": 0.60, "banana": 0.35}$ >>> prices["banana"]$ 0.35Each entry is a key and a value. The key is what you look up with; the value is what you get back. Keys are unique — assign to a key that already exists, and you overwrite the value, you do not add a second entry. Assign to a key that does not exist yet, and Python creates it on the spot.
$ >>> prices["banana"] = 0.40$ >>> prices["mango"] = 0.90$ >>> prices$ {'apple': 0.6, 'banana': 0.4, 'mango': 0.9}Notice mango lands at the end, not sorted alphabetically and not sorted by price. A dictionary keeps keys in the order they were first inserted, and that has been a guarantee of the language since Python 3.7 — not an accident you happened to observe once.
Look something up by key:
>>> prices['apple']
'0.60'
What makes a key valid
A dictionary works by turning each key into a number the moment it is stored, and looking for that same number again on every lookup. That trick is called hashing, and it only works if the key can never change after its number has been computed — otherwise the dictionary would be searching for the wrong number the next time you asked.
Strings, numbers, and tuples qualify, because none of them can be edited in place. A tuple of coordinates makes a perfectly good key for exactly that reason.
$ >>> board = {(0, 0): "empty", (0, 1): "empty"}$ >>> board[(0, 0)]$ 'empty'$ >>> cache = {}$ >>> cache[[1, 2]] = "nope"$ TypeError: unhashable type: 'list'A list fails outright, and on purpose. A list can change after you build it — append to it, and the same object now means something different. Python will not let a value that can shift underneath you sit at the front of a lookup table, so it raises TypeError the instant you try, rather than let a key quietly go stale.
Strings, numbers, tuples, frozensets — anything Python considers immutable.
Lists, dictionaries, sets — anything whose contents can change after creation.
A missing key is not zero, it is an error
Ask for a key that is not there, and Python does not return None or 0 to be helpful. It raises KeyError and stops the program, the same way an undefined variable would.
$ >>> prices["mango"]$ 0.9$ >>> prices["fig"]$ KeyError: 'fig'Sometimes a KeyError is exactly what you want. A configuration dictionary that is missing "host" should not quietly hand back None and let the program limp on for another twenty lines before it fails somewhere confusing. Let it crash immediately, at the line with the typo, with the name of the missing key printed right there. Swallowing the error with .get() in that situation does not fix the bug — it just moves the failure somewhere harder to find.
Walking a dictionary's keys, values, and items
Loop over a dictionary directly, and Python hands you its keys, one at a time. The values are one lookup away from each one.
$ >>> for fruit in prices:$ ... print(fruit, prices[fruit])$ apple 0.6$ banana 0.4$ mango 0.9.items() skips the extra lookup and hands you both halves of each pair at once, which is almost always what you actually want inside a loop.
$ >>> for fruit, price in prices.items():$ ... print(f"{fruit}: {price}")$ apple: 0.6$ banana: 0.4$ mango: 0.9.keys() and .values() exist for when only one side matters — list(prices.values()) gets you just the numbers, with no fruit names attached to slow you down.
Nesting dictionaries inside dictionaries
A value in a dictionary can be anything, including another dictionary. That is how you model a record with more than one field per key, instead of juggling several dictionaries that all happen to share the same keys.
$ >>> users = {$ ... "ada": {"age": 36, "role": "admin"},$ ... "grace": {"age": 41, "role": "editor"},$ ... }$ >>> users["ada"]["role"]$ 'admin'Each level is looked up the same way, one bracket at a time. That is convenient right up until an outer key is missing — .get() on a missing key returns None, and None has no .get() of its own to chain onto.
$ >>> users.get("nobody").get("role")$ AttributeError: 'NoneType' object has no attribute 'get'When a dictionary is the right tool, and when a list still is
Neither structure is the better one in general — they answer different questions, and picking the wrong one shows up later as code that fights the shape of its own data.
Reach for a dictionary
When you look things up by a meaningful name — a username, a product code, a configuration setting, a record with several named fields. The name is the point; position is irrelevant.
Reach for a list
When what matters is order and position — a queue of tasks, a sequence of moves, a leaderboard, anything where first and next mean something.
Key takeaways
- A dictionary stores key/value pairs and looks up by key, not by position — and it keeps insertion order while doing it.
- Keys must be hashable: strings, numbers, and tuples work as keys; a list or another dictionary never can, on purpose.
- A missing key raises KeyError by default, and that is often the behaviour you want — .get() is for when a missing key is a normal outcome, not a bug you want to hide.
- .items() hands you both the key and the value in one loop, which is almost always what you actually want.
- Choose a dictionary when you look things up by name; choose a list when order and position are what matter.
Quick check
Answer these to unlock the next chapter — 3 of 4 to pass. You can retake it anytime.
Answer every question to check.
Make a free account to read on
Every chapter is free — an account is how your progress, XP, and streak follow you from your laptop to your phone, and how you show up on the leaderboard. No payment, no trial.