South Plantation High School logo
South Plantation High School · Procedural Programming with Python

Dictionaries

Building · Accessing by key · Adding, updating & removing · Missing keys · Lesson review

Review the lesson slides, then complete the assignment at the bottom of this page. Your assignment work is saved in this browser on this computer. When you check a task, your name, period, and which tasks are complete are shared with your teacher (your code is not). When you finish, print it or save it as a PDF to turn in.
1 / 17
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

WordWhat it meansExample
dictionary (dict)A collection of key-value pairs{"Ava": 15}
keyThe label you look things up by"Ava"
valueThe data stored under a key15
pair (item)One key together with its value"Ava": 15
KeyErrorThe error for looking up a key that is not thereages["Zed"]
defaultA backup value to use when a key is missingthe 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 { }.
ListDictionary
Brackets[15, 16, 14]{"Ava": 15, ...}
Look up byposition: ages[0]key: ages["Ava"]
Good foran ordered collectionlooking 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

WayExample
Curly bracesages = {"Ava": 15, "Ben": 16}
Empty, then add laterages = {}
dict() with namesdict(Ava=15, Ben=16)
dict() with pairsdict([("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?

SituationBest 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 unusualtry / 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
  1. Python runs the try block.
  2. If an error happens, Python stops the try block right there and jumps to a matching except block.
  3. If no error happens, the except block is skipped.
  4. 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

KeyLines that runOutput
"math"whole try block; except skippedlooking up math, 90, found it, the program keeps going
"art"first try line, then the lookup fails → except blocklooking 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 newadd a new pair
already existsupdate (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

CodeWhat it doesMissing key?
del d[key]removes the pairKeyError
d.pop(key)removes it and returns the valueKeyError
d.pop(key, default)same, but returns default if missingno error
d.popitem()removes and returns the last added pair as a tupleKeyError if empty
d.clear()removes everythingno 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

LoopGives 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"

chcounts.get(ch, 0)counts after
b0 (new){'b': 1}
o0 (new){'b': 1, 'o': 1}
o1{'b': 1, 'o': 2}
k0 (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

  1. student["address"] gives the inner dictionary {"city": "Plantation", "state": "FL"}.
  2. ["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

MistakeCodeWhat happens (Python 3.13)Fix
Wrong capitalizationages["ava"]KeyError: 'ava'Match the key exactly
Using a positionages[0]KeyError: 0Use the key: ages["Ava"]
Missing keyages["Dee"]KeyError: 'Dee'in, .get(), or try
Deleting a missing keydel ages["Dee"]KeyError: 'Dee'ages.pop("Dee", None)
Using a list methodages.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 viewages.keys()[0]TypeError: 'dict_keys' object is not subscriptablelist(ages)[0]
Adding keys in a loop over the dictfor k in d: d[k + "!"] = 1RuntimeError: dictionary changed size during iterationfor 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 keptUse 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

  1. Read the key in the error message. KeyError: 'ava' tells you exactly which key was not found.
  2. Print the keys: print(list(d)) shows what is really in the dictionary.
  3. 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.