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

Looping Through Dictionaries

keys() · values() · items() · Checking keys with in · 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

Looping Through Dictionaries

for name in grades:.keys().values().items()"Ava" in grades

Visit every key, every value, or every pair, and check whether a key exists before you use it.

By the end of this lesson you will be able to:

  • Iterate through a dictionary's keys with for key in d or d.keys().
  • Iterate through its values with d.values().
  • Iterate through its key-value pairs with d.items().
  • Use in (and not in) to check whether a key exists.
How to use this page: Use Next and Back (or the arrow keys). Watch the Loop Visualizer highlight each pair, test keys in the Key Detective, and build loops in the Loop Builder challenge.

Words you will hear today

WordWhat it meansExample
iterateVisit each item, one at a time, in a loopfor name in grades:
viewA live window into a dictionary's keys, values, or pairsgrades.keys()
unpackingSplitting a pair into two variablesfor name, grade in ...
membership testChecking whether something is inside with in"Ava" in grades
insertion orderThe order the pairs were first addedAva, Ben, Cy
  • keys, values, items, and in.
  • Iterate, view, unpacking, membership test, insertion order.

Last time we built dictionaries and looked up one value at a time. Today we visit every entry: all the keys, all the values, or all the pairs. And we practice the safest habit with dictionaries: checking that a key exists before we use it.

Review

Quick review: one dictionary, three views

grades = {"Ava": 92, "Ben": 85, "Cy": 77}
MethodGives a view of the...With grades
grades.keys()keysdict_keys(['Ava', 'Ben', 'Cy'])
grades.values()valuesdict_values([92, 85, 77])
grades.items()(key, value) pairsdict_items([('Ava', 92), ('Ben', 85), ('Cy', 77)])

Python describes keys() as returning "a set-like object providing a view on the dict's keys." A view is a live window into the dictionary, not a separate copy.

  • All three work in a for loop.
  • All three go in insertion order: the order the pairs were added.
  • Wrap a view in list() when you need a real list: list(grades.keys()).
  • A view is not a list, so grades.keys()[0] is TypeError: 'dict_keys' object is not subscriptable.
In this lesson, every example uses the same gradebook so you can compare the loops side by side.

How to pick the right view

Ask yourself what the loop needs to know on each pass:

  • Only the names (keys)? → loop over the dictionary itself, or .keys().
  • Only the numbers (values)? → .values().
  • Both, together? → .items().

Why "insertion order" matters: if you add Ava, then Ben, then Cy, every loop visits them in that order. Updating Ben's grade does not move him. Only removing a key and adding it back puts it at the end.

  • keys(), values(), items() give views.
  • Insertion order. list() to make a list.
  • Need keys, values, or both? That picks the view.

Every dictionary gives you three windows into it. Keys shows the names, values shows the grades, and items shows the name and grade together as pairs. They are called views because they look into the dictionary rather than copying it. If the dictionary changes, the view changes too.

Keys

Looping over the keys

A plain for loop over a dictionary gives you each key:

for name in grades:
    print(name)
Ava Ben Cy

Need the value too? Use the key to look it up:

for name in grades:
    print(name, "scored", grades[name])
for name in grades: and for name in grades.keys(): do the same thing. Many programmers write .keys() anyway because it makes the code easier to read.

What happens on each pass

  1. Pass 1: name is "Ava". grades[name] looks up 92.
  2. Pass 2: name is "Ben". grades[name] looks up 85.
  3. Pass 3: name is "Cy". grades[name] looks up 77.

The lookup inside the loop is always safe, because every key the loop gives you is guaranteed to be in the dictionary.

Keys are perfect for a roster or a menu: print every name, or every item someone can order, without needing the values at all.

  • for key in d: gives keys.
  • d[key] inside the loop gives the value.
  • d[key] inside a key loop never causes a KeyError.

When you loop over a dictionary directly, Python hands you the keys, one at a time, in the order they were added. Inside the loop, the key is all you need to look up the value with square brackets. Step through and watch the name variable change.

Keys

Working with .keys()

CodeResult
grades.keys()dict_keys(['Ava', 'Ben', 'Cy'])
list(grades.keys())['Ava', 'Ben', 'Cy']
len(grades.keys())3
sorted(grades.keys())a sorted list of keys
Views are live. If you save names = grades.keys() and then add a new student, names shows the new key too, without calling .keys() again.

Step through and watch what names shows before and after a new key is added.

What "live" means

A view is not a snapshot; it always shows the dictionary as it is right now. In the demo, names was created before Di was added, yet printing it afterward includes Di. A list made with list(grades.keys()) is different: it is a copy taken at that moment and does not update.

Sorting keys: sorted(grades) gives a new alphabetical list without changing the dictionary's order. To sort names by their grades instead, use sorted(grades, key=grades.get, reverse=True), which gives ['Ava', 'Ben', 'Cy'] (highest grade first).

  • keys() view; list(), len(), sorted() work on it.
  • Views update automatically.
  • A view stays current; list(d.keys()) is a snapshot.

The keys method gives a view of the keys. You can count it, sort it, or turn it into a list. The surprising part is that a view stays connected to the dictionary. Step through: we save the view, add Di, and the saved view now includes Di.

Values

Looping over the values with .values()

for grade in grades.values():
    print(grade)
92 85 77

Values are perfect for math on the whole dictionary:

total = sum(grades.values())          # 254
average = total / len(grades)         # 84.666...
print(max(grades.values()))           # 92
print(min(grades.values()))           # 77
Values do not know their keys. Inside for grade in grades.values(): you cannot tell who earned that grade. If you need the name too, use .items() (next slide).

Counting with values

passing = 0
for grade in grades.values():
    if grade >= 80:
        passing += 1
print(passing, "students scored 80 or higher")   # 2

This is the counting pattern you already know, applied to the values. Because you only need the numbers, .values() is the simplest choice.

Check for an empty dictionary before computing an average: len(grades) of 0 would cause a ZeroDivisionError, and max() of no values is a ValueError.

  • values() for totals, averages, max, min.
  • No way back to the key from a value.
  • values() + counting pattern = how many match.

When you only care about the numbers, loop over values. Sum, max, and min all work directly on the values view. But values are only half the story. If you find the highest grade, values cannot tell you whose grade it was.

Key-Value Pairs

Looping over pairs with .items()

The tutorial: "When looping through dictionaries, the key and corresponding value can be retrieved at the same time using the items() method."

for name, grade in grades.items():
    print(name, "scored", grade)
Ava scored 92 Ben scored 85 Cy scored 77
  • Each item is a tuple: ('Ava', 92).
  • name, grade unpacks the tuple, just like x, y = point from the tuples lesson.
  • Pick names that describe the pair: for student, grade in ..., for item, price in ....
Forgetting .items() is a common bug: for name, grade in grades: tries to unpack each key (a string), which usually gives ValueError.

What .items() hands the loop

On each pass, Python gets one tuple, like ('Ben', 85), and splits it: name = 'Ben', grade = 85. It is exactly the same as writing name, grade = ('Ben', 85).

A sneaky version of the bug

Without .items(), Python tries to unpack each key string into two names. "Ava" has 3 letters, so it fails. But "Cy" has exactly 2 letters, so it "works": name becomes 'C' and grade becomes 'y'! If you ever see single letters where you expected names and numbers, check for a missing .items().

  • for k, v in d.items(): key and value together.
  • Each item is a tuple that gets unpacked.
  • Missing .items() can split two-letter keys into letters.

Items is the one you will use most. It gives you each key and value together as a pair, and the two names after for unpack the pair. Try the option without items to see the error you get when you forget it.

Practice

Loop Visualizer

Pick a loop and click Next (or Play). The highlighted row is the pair Python is visiting; the variables and output update at each step. Nothing is recorded.

Compare the three loops

LoopPass 1 variable(s)
for name in grades:name = 'Ava'
for g in grades.values():g = 92
for name, g in grades.items():name = 'Ava', g = 92

All three visit the pairs in the same order. The only difference is what you get to hold on each pass.

  • Compare what the loop variable holds in each style.

Run the same dictionary through all four loops. In the keys loop, the variable holds a name. In the values loop, it holds a number. In the items loop, two variables hold both. Notice that every loop visits the rows in the same top-to-bottom order.

Formatting

Printing a neat report with f-strings

Loops over .items() often print a report. An f-string (a string with f in front) lets you put values right inside the text with { }:

for name, grade in grades.items():
    print(f"{name}: {grade}")
Ava: 92 Ben: 85 Cy: 77
Inside the bracesMeaningExample result
{name:<6}left-align in 6 spaces'Ava '
{grade:>4}right-align in 4 spaces' 92'
{avg:.1f}1 digit after the decimal point'84.7'

Reading a format code

Everything after the colon inside { } is a format instruction:

  • < means left-align, > right-align, and the number is the total width. Text usually looks best left-aligned and numbers right-aligned, so the digits line up.
  • .1f means a float with 1 digit after the decimal point. .2f is good for money.
  • You can combine them: {avg:>6.1f} right-aligns in 6 spaces with 1 decimal place.

Quotes inside an f-string: if the f-string uses double quotes, use single quotes for any string inside the braces, like f"{'Name':<6}".

  • f"{name}: {grade}" puts values in text.
  • :<6, :>4, :.1f for alignment and decimals.
  • < left, > right, number = width, .1f = 1 decimal.

Printing pairs is so common that it is worth learning f-strings now. Put an f before the quotes, and anything in curly braces is replaced by its value. After a colon you can add formatting: less-than means left-align, greater-than means right-align, and point one f means one digit after the decimal point.

Key Existence

Does a key exist? Use in

The tutorial: "To check whether a single key is in the dictionary, use the in keyword."

"Ava" in grades       # True
"Dee" in grades       # False
"Dee" not in grades   # True
  • The result is a boolean, so it fits right into an if.
  • The Python reference says that for a dictionary, "in tests whether the dictionary has a given key." It checks keys only, not values: 92 in grades is False.
  • It is case sensitive: "ava" in grades is False.
name = input("Student: ")
if name in grades:
    print(name, "has", grades[name])
else:
    print("No grade for", name)

Type a name and run. The if keeps the program safe from a KeyError.

Why check before you look up?

A program that uses user input cannot know in advance which names someone will type. One typo would crash the program with a KeyError. The if name in grades: check turns that crash into a friendly message.

Making the check friendlier

Users often type extra spaces or different capitals. Cleaning the input first helps, as long as your keys are stored the same way:

name = input("Student: ").strip().title()   # " ava " becomes "Ava"
if name in grades:
    print(name, "has", grades[name])
  • key in d: True/False. not in: the opposite.
  • Checks keys only, case sensitive.
  • Clean input with .strip().title() before checking.

Before you use square brackets on a key that might be missing, ask first. The in keyword answers yes or no: is this key in the dictionary? Put it in an if statement, and your program never crashes with a KeyError. Try a name that is there, one that is not, and one with the wrong capitalization.

Key Existence

Checking values and pairs

QuestionCodeResult
Is there a key "Ben"?"Ben" in gradesTrue
Did anyone score 85?85 in grades.values()True
Is 85 a key?85 in gradesFalse
Did Ben score exactly 85?("Ben", 85) in grades.items()True
Is "Dee" missing?"Dee" not in gradesTrue
Speed note: checking a key is what dictionaries are built for. Checking .values() has to look through the values one by one, so it is slower on very large dictionaries.

Finding who had a certain value

85 in grades.values() tells you whether someone scored 85, but not who. To find the name, loop over the pairs:

for name, grade in grades.items():
    if grade == 85:
        print(name, "scored 85")      # Ben scored 85

More than one student might have the same value, so this loop can find several matches. Keys, on the other hand, are always unique.

  • in d / in d.keys(): keys. in d.values(): values. in d.items(): exact pairs.
  • To find who has a value, loop over .items().

A plain in checks keys. If you want to know whether a value appears anywhere, check the values view. And to check an exact pair, check the items view with a tuple. Run each option and compare.

Practice

Key Detective

Type a key (text needs quotes) and click Investigate. See what in, square brackets, and .get() each do with it. Nothing is recorded.

What each tool does with a missing key

ToolKey presentKey missing
key in dTrueFalse
d[key]the valueKeyError
d.get(key)the valueNone

Try 92 as the key to see that values are not keys.

  • Try "Ava", "ava", "Dee", and 92.

Use the detective to compare the three ways of handling a key. When the key exists, all three agree. When it is missing, in says False, square brackets crash with a KeyError, and get quietly gives back the default.

Patterns

Common patterns

GoalPattern
Find who scored highestloop .items(), remember the best pair
Count matchesloop .values(), add 1 when the if is True
Filter into a listloop .items(), append the keys you want
Print in alphabetical orderfor name in sorted(grades):
Build a new dictionaryloop .items(), store into a new dict

The tutorial: "To loop over a sequence in sorted order, use the sorted() function which returns a new sorted list while leaving the source unaltered."

Building a new dictionary from an old one

curved = {}
for name, grade in grades.items():
    curved[name] = grade + 5
print(curved)    # {'Ava': 97, 'Ben': 90, 'Cy': 82}

Python also has a one-line dictionary comprehension for this, just like list comprehensions: {name: grade + 5 for name, grade in grades.items()}. Add an if at the end to filter: {n: g for n, g in grades.items() if g >= 80} keeps only Ava and Ben.

  • Highest, count, filter, sorted, new dict.
  • Build a new dict in a loop, or with {k: v for k, v in d.items()}.

Most dictionary loops follow a handful of patterns. Step through each one. The "find the highest" pattern is worth memorizing: start with nothing, and every time you see a better grade, remember that name and grade.

Careful

Changing a dictionary while looping

Inside the loop...Safe?
change an existing value: grades[name] += 5Yes
add a new keyNo: RuntimeError
delete a keyNo: RuntimeError

The error is RuntimeError: dictionary changed size during iteration.

Fixes: loop over a copy of the keys with for name in list(grades):, or build a new dictionary. The tutorial's advice for lists applies here too: "it is often simpler and safer to create a new list instead."

Why adding keys during a loop is a problem

The loop is walking through the dictionary's keys in order. If the dictionary grows or shrinks partway through, Python can no longer be sure which keys it has visited, so it stops with an error instead of guessing.

The safe way to remove failing students

for name in list(grades):      # a snapshot of the keys
    if grades[name] < 80:
        del grades[name]           # safe: we loop over the snapshot

list(grades) makes a separate list of the keys before the loop starts, so deleting from the dictionary does not disturb the loop.

  • Changing values: fine. Adding or removing keys: RuntimeError.
  • Loop over list(d) or build a new dict.
  • Loop over list(d) when deleting keys.

Changing a value during a loop is fine, because the set of keys stays the same. But adding or removing keys while Python is walking through them causes a RuntimeError. The fix is to loop over a list copy of the keys, or to build a brand new dictionary with only what you want.

Practice

Loop Builder challenge

Fill in the blanks with the dropdowns so each program prints exactly the target output, then click Check. The code really runs. Practice only; nothing is recorded.

Strategy

  1. Look at the target output. Does each line show a key, a value, or both?
  2. Pick d/.keys(), .values(), or .items() to match.
  3. Is the order alphabetical? Then you need sorted().
  4. Are only some entries shown? Then you need an if inside the loop.
  • Match the loop style to what the target output needs.

Look at each target output first. Does it show names, grades, or both? That tells you whether you need keys, values, or items. Then make sure the print uses the right variable.

Debugging

Common mistakes

MistakeCodeWhat happens (Python 3.13)Fix
Two names without .items()for k, v in grades:ValueError: too many values to unpack (expected 2)for k, v in grades.items():
Expecting values from a plain loopfor g in grades: total += gTypeError: unsupported operand type(s) for +=: 'int' and 'str'for g in grades.values():
Indexing a viewgrades.keys()[0]TypeError: 'dict_keys' object is not subscriptablelist(grades)[0]
Checking a value with plain in92 in gradesNo error. False, because 92 is not a key92 in grades.values()
Wrong capitalization"ava" in gradesNo error. FalseMatch the key exactly
Using a key without checkinggrades["Dee"]KeyError: 'Dee'if "Dee" in grades: first
Adding keys inside the loopfor k in grades: grades[k + "2"] = 0RuntimeError: dictionary changed size during iterationfor k in list(grades):
Forgetting the parenthesesfor v in grades.values:TypeError: 'builtin_function_or_method' object is not iterablegrades.values()

Try these at the prompt:

  • g = {"Ava": 92, "Ben": 85}
  • g.items()
  • 92 in g
  • 92 in g.values()
  • "Ben" not in g
  • list(g)
  • for k, v in g.items():
        print(f"{k}: {v}")

Reading the error messages

  • "too many values to unpack" or "not enough values": you wrote two loop names but forgot .items().
  • "unsupported operand ... 'int' and 'str'": you are doing math on keys (strings) instead of values.
  • "not subscriptable": you used [0] on a view. Wrap it in list().
  • "not iterable": you forgot the () after .values, .keys, or .items.
  • No error, but False: plain in only checks keys, and checks capitals exactly.
  • Most bugs: wrong view, or forgetting .items().
  • "not iterable" usually means missing () after .values/.keys/.items.

Most dictionary loop bugs come from using the wrong view. Ask: do I need keys, values, or both? And remember that a plain in only looks at keys. Checking for a value needs the values view.

Practice

Check yourself

Every question uses grades = {"Ava": 92, "Ben": 85, "Cy": 77}. Choose an answer, then click Check. Practice only; nothing is graded or recorded.

For each question, ask:
  • Which view is the loop using: keys, values, or items?
  • What does the loop variable hold on the first pass?
  • For in: is it checking keys (plain), values, or exact pairs?
  • Keys, values, or pairs? Then trace.

For each question, first decide whether the loop or check is looking at keys, values, or pairs. Then trace through the three students in order.

Summary

Cheat sheet

Loop over...

for name in grades:          # keys
for name in grades.keys():   # keys
for g in grades.values():    # values
for name, g in grades.items():  # pairs
for name in sorted(grades):  # keys, A-Z

Check if it exists

"Ava" in grades           # key?
"Dee" not in grades       # key missing?
92 in grades.values()     # value?
("Ava", 92) in grades.items()

if name in grades:
    print(grades[name])

Handy with views

list(grades)              # list of keys
len(grades)               # how many
sum(grades.values())      # total
max(grades.values())      # highest
print(f"{name}: {g}")     # neat output

Remember:

  • A plain loop or a plain in works with keys
  • Need both key and value? Use .items() and two loop names
  • Check with in before using d[key] on a key that might be missing
  • Do not add or remove keys while looping; loop over list(d) instead

Key words to know

keys() · values() · items() · view · insertion order · unpacking · in / not in · f-string · format code · dictionary comprehension · RuntimeError

  • keys, values, items, in.
  • Know the key words: view, items, unpacking, in, f-string.

Let's wrap up. Looping over a dictionary gives keys by default. Use values for the numbers and items for both. And before you use a key that might not exist, check it with in. These four tools cover almost every dictionary loop you will write.