Lesson Review
Dictionaries
{"Ava": 15}ages["Ava"]ages["Cy"] = 14del ages["Ben"]ages.get("Dee", 0)
Look things up by name instead of by position.
By the end of this lesson you will be able to:
- Construct dictionaries from key: value pairs.
- Access values by key.
- Add, update, and remove key-value pairs.
- Handle missing keys with
in, .get(), and try / except KeyError.
How to use this page: Use Next and Back (or the arrow keys). Run the editors, click keys in the dictionary viewer, experiment in the Dictionary Lab, and try the gradebook challenge.
Words you will hear today
| Word | What it means | Example |
dictionary (dict) | A collection of key-value pairs | {"Ava": 15} |
| key | The label you look things up by | "Ava" |
| value | The data stored under a key | 15 |
| pair (item) | One key together with its value | "Ava": 15 |
| KeyError | The error for looking up a key that is not there | ages["Zed"] |
| default | A backup value to use when a key is missing | the 0 in ages.get("Zed", 0) |
- Build, access, add/update/remove, handle missing keys.
- Key, value, pair, KeyError, default.
Lists find things by position: item zero, item one, item two. But what if you want to find a student's grade by their name, or a word's definition by the word itself? That is what dictionaries are for. Today we build them, look things up, change them, and learn what to do when a key is not there.
Dictionaries
What is a dictionary?
The Python tutorial: "It is best to think of a dictionary as a set of key: value pairs, with the requirement that the keys are unique (within one dictionary)."
ages = {"Ava": 15, "Ben": 16, "Cy": 14}
# key value
- A key is the label you look up (like a word in a paper dictionary).
- A value is what is stored under that key (like the word's definition).
- Each pair is written
key: value, and pairs are separated by commas, inside curly braces { }.
| List | Dictionary |
| Brackets | [15, 16, 14] | {"Ava": 15, ...} |
| Look up by | position: ages[0] | key: ages["Ava"] |
| Good for | an ordered collection | looking things up by a name or ID |
Real-world dictionaries: a phone's contacts (name → number), a menu (item → price), a gradebook (student → grade), a game's inventory (item → count).
Why not just use two lists?
You could keep names in one list and ages in another, matched by position. But to find Cy's age you would have to search the names list for Cy, get its index, then use that index in the ages list. And if one list gets sorted or changed, the two no longer match up.
A dictionary keeps each key glued to its value, and it can jump straight to a key without searching through everything. Even with a million entries, ages["Cy"] is about as fast as with three.
How to read it out loud: ages["Ava"] is "the value stored under the key Ava in ages," or simply "Ava's age."
- key: value pairs in { }. Keys are unique.
- Look up by key, not by position.
- A dictionary keeps each key glued to its value and finds it instantly.
A dictionary stores pairs. The key is what you search for, and the value is what you get back. In a real dictionary you look up a word and get its definition. In a Python dictionary you might look up a student's name and get their age. Keys must be unique: one dictionary cannot have two Avas.
Constructing
Building a dictionary
| Way | Example |
| Curly braces | ages = {"Ava": 15, "Ben": 16} |
| Empty, then add later | ages = {} |
dict() with names | dict(Ava=15, Ben=16) |
dict() with pairs | dict([("Ava", 15), ("Ben", 16)]) |
- Values can be anything: numbers, strings, lists, even other dictionaries.
- Keys are usually strings or numbers. The tutorial: keys "can be any immutable type," so tuples work too, but "You can't use lists as keys."
- Python prints a dictionary the same way you write one, with the pairs in the order they were added.
Duplicate keys: {"a": 1, "a": 2} gives no error. The second value replaces the first, leaving {'a': 2}.
Building a dictionary in a loop
Often you start empty and fill it as your program runs:
prices = {}
prices["taco"] = 2.50
prices["burrito"] = 7.25
print(prices) # {'taco': 2.5, 'burrito': 7.25}
Formatting a longer dictionary: like long lists, you can spread a dictionary over several lines, one pair per line, until the closing }. It is much easier to read and to add pairs later.
Careful with {}: empty curly braces make an empty dictionary, not an empty set or list.
- { key: value, ... }, {} for empty, dict() also works.
- Keys must be immutable; values can be anything.
- Start with {} and add pairs as the program runs.
The most common way to build a dictionary is with curly braces, a colon between each key and value, and commas between pairs. An empty pair of braces makes an empty dictionary that you can fill later. Try the last two options in the editor: a duplicate key, and a list used as a key.
Accessing
Getting a value by its key
Put the key in square brackets: ages["Ava"].
ages = {"Ava": 15, "Ben": 16}
print(ages["Ava"]) # 15
print(ages["Cy"]) # KeyError: 'Cy'
- Keys are case sensitive:
ages["ava"] is a KeyError.
- There are no positions:
ages[0] looks for a key named 0, so it is KeyError: 0.
- The tutorial: "Extracting a value for a non-existent key by subscripting (
d[key]) raises a KeyError."
Click a key in the viewer, or type any key (with quotes for text) and press Look up.
Brackets mean something different here
With a list, the thing in brackets is a position. With a dictionary, it is a key. That is why ages[0] does not mean "the first person"; Python searches for a key that is literally the number 0.
Using a variable as the key
name = input("Whose age? ")
print(ages[name])
Notice there are no quotes around name in the brackets: we want the value of the variable (whatever the user typed), not the word "name."
- d[key] gets the value.
- Missing key: KeyError.
- In a dict, brackets hold a key, not a position.
To get a value, put the key in square brackets, just like a list index, except the key is a name instead of a number. Dictionaries are strict: the key must match exactly, including capital letters. If the key is not there, Python stops with a KeyError that shows the key it could not find.
Missing Keys
Three ways to handle a missing key
1. Check first with in
if name in ages:
print(ages[name])
else:
print(name, "not found")
2. Use .get() with a default
print(ages.get(name)) # None if missing
print(ages.get(name, 0)) # 0 if missing
The tutorial: "use the get() method instead, which returns None (or a specified default value) if the key is not in the dictionary."
3. Try it, and catch the KeyError
try:
print(ages[name])
except KeyError:
print(name, "not found")
Pick a method and a name. Try a name that is in the dictionary (Ava) and one that is not (Cy).
Which way should I choose?
| Situation | Best tool |
| Different actions for "found" and "not found" | if key in d: |
| A sensible default value is fine (like 0 or "unknown") | d.get(key, default) |
| The key is almost always there, and missing is unusual | try / except KeyError |
get() does not add the key. After ages.get("Dee", 0), the dictionary still has no "Dee". It just hands you the default for that one lookup.
in checks keys only: 15 in ages is False even if someone is 15. To search values, use 15 in ages.values().
- in: ask first. get: default value. try/except: catch the error.
- get() does not add the key.
- in checks keys, not values.
A missing key does not have to crash your program. You can ask first with in. You can use get, which quietly gives back None or a default you choose. Or you can try the lookup and catch the KeyError if it happens. Run the "no check" option to see what happens when you do none of these.
Missing Keys
How try / except works
try:
# code that might cause an error
except KeyError:
# runs only if a KeyError happened
- Python runs the
try block.
- If an error happens, Python stops the try block right there and jumps to a matching
except block.
- If no error happens, the
except block is skipped.
- Either way, the program keeps going after the whole
try / except.
except KeyError as e: saves the error in e. Printing e shows the missing key, like 'art'.
- Name the error you expect (
KeyError). A bare except: catches everything, which can hide real bugs.
Step through with each key. Watch which lines run and which are skipped.
Trace both paths
| Key | Lines that run | Output |
"math" | whole try block; except skipped | looking up math, 90, found it, the program keeps going |
"art" | first try line, then the lookup fails → except block | looking up art, missing key: 'art', the program keeps going |
Notice that print("found it") never runs for "art". Once an error happens, the rest of the try block is skipped.
Keep the try block small. Put only the line that might fail inside it. A big try block can accidentally catch errors from other lines that you did not expect.
- Error in try: jump to except. No error: skip except.
- Catch the specific error you expect.
- After an error, the rest of the try block is skipped.
- Keep try blocks small.
Step through with the key art. Python prints "looking up art," then the lookup fails, so "found it" never prints. Python jumps straight to the except block, prints the missing key, and then keeps going. Now switch to math: no error happens, so the except block is skipped completely.
Changing
Adding and updating pairs
The same line does both jobs: d[key] = value.
| If the key... | d[key] = value will... |
| is new | add a new pair |
| already exists | update (replace) its value |
The tutorial: "If you store using a key that is already in use, the old value associated with that key is forgotten."
inventory = {"apples": 3}
inventory["pears"] = 5 # add
inventory["apples"] = 10 # update
inventory["apples"] += 2 # update using the old value
inventory.update({"plums": 4, "pears": 6}) # add/update many
d[key] += 1 needs the key to exist first. On a missing key it is a KeyError.
Step through and watch inventory in the Variables panel.
Add or update: Python decides for you
d[key] = value does not ask whether the key exists. If it is new, a pair is added; if it already exists, the old value is simply replaced, with no warning. If you need to protect existing data, check first:
if "apples" not in inventory:
inventory["apples"] = 0 # only set it the first time
Why += needs the key
inventory["kiwis"] += 1 really means inventory["kiwis"] = inventory["kiwis"] + 1. The right side reads inventory["kiwis"] first, and that lookup fails if the key is not there yet. The counting pattern on slide 11 solves this.
- d[key] = value adds or updates.
- update() adds or updates many pairs.
- d[key] = value replaces old values with no warning.
Adding and updating use exactly the same syntax. If the key is new, Python adds a new pair. If the key is already there, Python throws away the old value and stores the new one. Dictionaries do not have append, because every value needs a key.
Changing
Removing pairs
| Code | What it does | Missing key? |
del d[key] | removes the pair | KeyError |
d.pop(key) | removes it and returns the value | KeyError |
d.pop(key, default) | same, but returns default if missing | no error |
d.popitem() | removes and returns the last added pair as a tuple | KeyError if empty |
d.clear() | removes everything | no error |
Python's own description of popitem(): "Pairs are returned in LIFO (last-in, first-out) order."
Choosing a removal tool
- Just remove it:
del d[key]
- Remove it and use the value:
x = d.pop(key), for example to move an item from one dictionary to another.
- Remove it if it is there, no error either way:
d.pop(key, None)
- Start over:
d.clear() empties the dictionary but keeps the same dictionary object.
LIFO means "last in, first out," like a stack of plates: the plate put on most recently is the first one taken off. popitem() removes the most recently added pair.
- del and pop remove by key. pop returns the value.
- pop(key, default) never errors.
- pop(key, None) removes if present, never errors.
To remove a pair, use del or pop with the key. Pop is handy because it gives you the value as it removes it. If you are not sure the key exists, give pop a default value and it will never raise a KeyError. Try each option, including the ones with kiwi, which is not in the dictionary.
Practice
Dictionary Lab
Pick an operation, fill in the key (and value), and click Run. Every line runs on the same dictionary. Put text in quotes, like "kiwi". Nothing is recorded.
Experiments to try
- Add a key, then add it again with a different value. How many pairs are there?
- Look up a missing key with brackets, then with
get. Compare the results.
- Use
pop on a key and see what value comes back.
- Try
"Kiwi" and "kiwi". Are they the same key?
- Predict before each Run. Cause a KeyError on purpose, then avoid it with get or pop with a default.
This is your sandbox. Add a fruit, update a count, delete one, and look things up. Then try to cause a KeyError on purpose, and fix it with get or with pop and a default.
Looping
Looping through a dictionary
| Loop | Gives you |
for k in d: | each key |
for v in d.values(): | each value |
for k, v in d.items(): | each pair, unpacked |
for k in sorted(d): | keys in sorted order |
- Keys come out in the order they were added. The tutorial:
list(d) gives "all the keys used in the dictionary, in insertion order."
d.keys(), d.values(), and d.items() are views. Wrap them in list() to get a list.
Do not add or remove keys while looping over a dictionary: RuntimeError: dictionary changed size during iteration. Loop over list(d) instead. (Changing the values is fine.)
Looping by key gives you the value too
for k in d: gives only keys, but you can always get the value with d[k]. Still, .items() is clearer when you need both:
for subject, score in scores.items():
print(f"{subject}: {score}")
Finding the biggest value
best = None
for subject, score in scores.items():
if best is None or score > scores[best]:
best = subject
print("Best subject:", best) # music
A shortcut that does the same: max(scores, key=scores.get).
- for k in d: keys. .items(): pairs. .values(): values.
- Don't change the size while looping.
- .items() is clearest when you need keys and values.
- max(d, key=d.get) finds the key with the biggest value.
Looping over a dictionary gives you the keys, in the order they were added. If you want both the key and the value, loop over items and unpack each pair, just like we did with tuples. Try the last option to see what happens when a loop tries to add new keys.
Patterns
A classic pattern: counting
Dictionaries are perfect for counting things. Two common ways:
Check with in
counts = {}
for ch in word:
if ch in counts:
counts[ch] += 1
else:
counts[ch] = 1
Shorter with .get()
counts = {}
for ch in word:
counts[ch] = counts.get(ch, 0) + 1
counts.get(ch, 0) means "the count so far, or 0 if this is the first time." That is why it never raises a KeyError.
Type any word and step through the loop.
Trace for the word "book"
ch | counts.get(ch, 0) | counts after |
| b | 0 (new) | {'b': 1} |
| o | 0 (new) | {'b': 1, 'o': 1} |
| o | 1 | {'b': 1, 'o': 2} |
| k | 0 (new) | {'b': 1, 'o': 2, 'k': 1} |
The same pattern counts anything: votes for each candidate, how many times each word appears in a story, or how often each number came up on a die.
- counts[x] = counts.get(x, 0) + 1
- The counting pattern works for votes, words, dice rolls, anything.
Counting is one of the most useful dictionary patterns. Each letter is a key, and its value is how many times we have seen it. The get method with a default of zero handles the first time we see a letter, so we never hit a KeyError. Try your name, or a word like Mississippi.
Nesting
Dictionaries with lists and dictionaries inside
student = {
"name": "Ava",
"grades": [90, 85, 77],
"address": {"city": "Plantation", "state": "FL"}
}
student["grades"][0] # 90
student["address"]["city"] # 'Plantation'
student["grades"].append(88) # change the list inside
- Read chained brackets left to right: first the key, then whatever is stored there.
- A list of dictionaries is common too:
students = [{"name": "Ava"}, {"name": "Ben"}], so students[1]["name"] is 'Ben'.
Reading student["address"]["city"] step by step
student["address"] gives the inner dictionary {"city": "Plantation", "state": "FL"}.
["city"] looks up "city" in that dictionary: 'Plantation'.
Each set of brackets works on whatever the previous step produced. If the previous step gave a list, the next brackets need an index; if it gave a dictionary, they need a key.
Real data looks like this. Information from websites and apps (often in a format called JSON) is mostly dictionaries and lists nested inside each other.
- d[key][index] or d[key][key2]. Read left to right.
- Each bracket works on what the previous step produced.
Values can be anything, including lists and other dictionaries. That lets one variable describe a whole student. To reach inside, chain the brackets: first get the grades list, then index into it. Try the last option to see which key Python reports as missing.
Keys
Rules for keys
- Keys must be unique. Storing an existing key replaces its value.
- Keys must be immutable: strings, numbers, booleans, and tuples (of immutable things) work.
- Lists cannot be keys, because they can change:
TypeError: unhashable type: 'list'.
- Values have no rules: any type, and duplicates are fine.
This is the dictionary-key reason from the tuples lesson: a tuple like (3, 4) can be a key for a map of a game board, but a list like [3, 4] cannot.
Picking good keys: use something unique that you will search by, like a username or student ID. Two students may share a first name, but not an ID.
Why must keys be immutable?
A dictionary finds a key quickly by turning it into a number (called a hash) that says where to look. If a key could change after being stored, its hash would change too, and the dictionary would look in the wrong place and never find it again. Keeping keys unchangeable keeps lookups reliable.
Values are free: two students can both have the value 15. Only the keys need to be unique.
- Keys: unique and immutable. Values: anything.
- Keys must not change, or the dictionary could not find them again.
Keys have two rules: they must be unique, and they must be immutable. That is why strings and numbers make great keys, tuples work, and lists do not. Values have no rules at all. Try each option, especially the list key and the duplicate key.
Practice
Gradebook challenge
Write a Python expression that uses gradebook to produce each answer, then click Check. Practice only; nothing is recorded.
Strategy
- First, look at the structure: what are the keys, and what kind of value is stored under each (a number, a list, another dictionary)?
- Build the expression one bracket at a time and check what each step gives.
- For "how many," try
len(). For totals of a list, try sum().
- Use brackets for keys you know exist; get() for keys that might be missing.
Each task needs one expression. For Dee, remember that Dee is not in the gradebook, so square brackets would crash. Which tool gives back a default instead?
Debugging
Common mistakes
| Mistake | Code | What happens (Python 3.13) | Fix |
| Wrong capitalization | ages["ava"] | KeyError: 'ava' | Match the key exactly |
| Using a position | ages[0] | KeyError: 0 | Use the key: ages["Ava"] |
| Missing key | ages["Dee"] | KeyError: 'Dee' | in, .get(), or try |
| Deleting a missing key | del ages["Dee"] | KeyError: 'Dee' | ages.pop("Dee", None) |
| Using a list method | ages.append(17) | AttributeError: 'dict' object has no attribute 'append' | ages["Dee"] = 17 |
| A list as a key | {["a"]: 1} | TypeError: unhashable type: 'list' | Use a string or tuple key |
| Indexing a view | ages.keys()[0] | TypeError: 'dict_keys' object is not subscriptable | list(ages)[0] |
| Adding keys in a loop over the dict | for k in d: d[k + "!"] = 1 | RuntimeError: dictionary changed size during iteration | for k in list(d): |
| Forgot a comma | {"a": 1 "b": 2} | SyntaxError: invalid syntax. Perhaps you forgot a comma? | {"a": 1, "b": 2} |
| Duplicate key | {"a": 1, "a": 2} | No error. Only {'a': 2} is kept | Use unique keys |
Try these at the prompt:
ages = {"Ava": 15}
ages["ava"]
ages.get("Dee", 0)
ages["Dee"] = 17
ages
ages.pop("Ava")
"Ava" in ages
ages.append(1)
KeyError checklist
- Read the key in the error message.
KeyError: 'ava' tells you exactly which key was not found.
- Print the keys:
print(list(d)) shows what is really in the dictionary.
- Compare carefully: capitals, extra spaces (
"Ava "), and types (the string "1" is not the number 1).
Keys from input() are always strings. If your dictionary uses number keys, convert first: d[int(choice)].
- KeyError is the big one. Check, get, or try.
- Print list(d) to see the real keys.
- input() gives strings; number keys need int().
Most dictionary errors are KeyErrors, and the message always shows you the exact key Python could not find. Check its spelling and capitalization first. If the key might truly be missing, use in, get, or try and except.
Practice
Check yourself
Choose an answer, then click Check. This is practice only. Nothing is graded or recorded.
For each question, ask:
- Is the key in the dictionary, spelled and capitalized exactly?
- Is this reading (brackets, get, in) or changing (assign, del, pop, update)?
- Brackets on a missing key: KeyError. get on a missing key: None or the default.
- Is the key there? That decides most answers.
For each question, first ask: is that key in the dictionary? Then decide whether the code reads, adds, updates, or removes.
Summary
Cheat sheet
Build and read
ages = {"Ava": 15, "Ben": 16}
empty = {}
ages["Ava"] # 15
len(ages) # 2
"Ava" in ages # True
for name, age in ages.items():
print(name, age)
Add, update, remove
ages["Cy"] = 14 # add
ages["Ava"] = 16 # update
ages.update({"Di": 15})
del ages["Ben"] # remove
x = ages.pop("Cy") # remove + return
ages.clear() # remove all
Missing keys
if "Dee" in ages:
print(ages["Dee"])
ages.get("Dee") # None
ages.get("Dee", 0) # 0
ages.pop("Dee", None)
try:
print(ages["Dee"])
except KeyError:
print("not found")
Remember:
- Keys are unique and immutable (strings, numbers, tuples), and case sensitive
d[key] = value adds a new pair or replaces the old value
d[key] on a missing key raises KeyError; get() does not
- Never add or remove keys while looping over the same dictionary
Key words to know
dictionary · key · value · pair / item · KeyError · get() · default · try / except · insertion order · immutable key · nesting
- Build, read, change, handle missing keys.
- Know the key words: key, value, KeyError, get, try/except.
Let's wrap up. Dictionaries store key: value pairs and let you look up values by key. The same bracket syntax adds or updates. Del and pop remove. And when a key might be missing, you have three tools: in, get, and try with except KeyError.