Warm-up · Activity 1 of 7
// B4.5 · ~35 min · Beginner
Dictionaries and looping techniques
After this lesson you can store and look up values by key, loop over dictionaries and sequences in the usual ways, and build a word counter from dicts and lists.
Predict · Activity 2 of 7
Predict before you read on: what does this print?
stock = {"apple": 3} stock["pear"] = 5 stock["apple"] = 1 print(stock)Practice · Activity 3 of 7
Fill in the default so that the loop counts each word.
counts = {} for word in ["a", "b", "a"]: counts[word] = counts.get(word, ____) + 1counts[word] = counts.get(word, ) + 1Practice · Activity 4 of 7
What does this print?
print(list(enumerate("ab", start=1)))Practice · Activity 5 of 7
With d = {"b": 2, "a": 1}, match each loop helper to the items it produces.
d = {"b": 2, "a": 1}Brain teaser · Activity 6 of 7
Brain teaser. Three names, two scores. What does this print?
names = ["ana", "ben", "cleo"] scores = [3, 5] print(dict(zip(names, scores)))Apply · Activity 7 of 7
Mini-task. Write invert(phone_book), which takes a dict from names to numbers and returns a new dict from numbers to names. Test it with {"ana": "0151", "ben": "0170"}. Then try two names with the same number, and explain what you see.
Check your work against this list
Build it yourself
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
A small grade book
A dict of grades, changed, read, looped over and ranked. sorted() with a key ranks the names by grade, and enumerate() numbers the places. zip() pairs two lists into new entries, and a dict comprehension filters the best grades. Change a grade or add a student and run it again.
main.py
grades = {"ana": 91, "ben": 78}
grades["cleo"] = 85 # a new key is added
grades["ben"] = 81 # an existing key gets a new value
print(grades)
print("ben" in grades, "dan" in grades, grades.get("dan", "no grade"))
for name, grade in grades.items():
print(name, grade)
ranking = sorted(grades, key=lambda name: grades[name], reverse=True)
for place, name in enumerate(ranking, start=1):
print(place, name)
newcomers = ["eve", "fay"]
marks = [88, 73]
print({name: mark for name, mark in zip(newcomers, marks)})
print({name: grade for name, grade in grades.items() if grade >= 85})
Run it with
python main.pyOutput
{'ana': 91, 'ben': 81, 'cleo': 85}
True False no grade
ana 91
ben 81
cleo 85
1 ana
2 cleo
3 ben
{'eve': 88, 'fay': 73}
{'ana': 91, 'cleo': 85}- ben keeps his position when his grade changes; only new keys go to the end.
- grades.get("dan", "no grade") returns the default instead of raising KeyError.
- The lambda tells sorted() to compare the grades, not the names.
Change it and run it
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Exercises
Exercise 1 of 3
Clean words
Step 1 of a word counter. words(text) should return the words of text in lower case, with punctuation stripped from both ends, and without pieces that were only punctuation. The starter only splits the text. word.strip(PUNCTUATION) removes those characters from both ends of a word.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
Lower-case the whole text first, then split it.
Hint 2
A list comprehension can strip every word; a second one can drop the empty strings.
Hint 3
cleaned = [word.strip(PUNCTUATION) for word in text.lower().split()], then return [word for word in cleaned if word]
Show a solution
One way to solve it. Yours can look different and still pass the checks.
PUNCTUATION = ".,!?;:"
def words(text: str) -> list[str]:
"""Split text into lower-case words without surrounding punctuation."""
cleaned = [word.strip(PUNCTUATION) for word in text.lower().split()]
return [word for word in cleaned if word]
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
PUNCTUATION = ".,!?;:"
def words(text: str) -> list[str]:
"""Split text into lower-case words without surrounding punctuation."""
return text.split()
test_main.py
from main import words
def test_lower_case():
"""Turns every word into lower case"""
got = words("The Cat")
assert got == ["the", "cat"], f"words('The Cat') returned {got!r}, expected ['the', 'cat']"
def test_punctuation():
"""Strips punctuation from the ends of words"""
got = words("Hello, world! Hello?")
assert got == ["hello", "world", "hello"], f"got {got!r}, expected ['hello', 'world', 'hello']"
def test_no_empty_words():
"""Drops pieces that were only punctuation"""
got = words("wait ... what")
assert got == ["wait", "what"], f"words('wait ... what') returned {got!r}, expected ['wait', 'what']"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyExercise 2 of 3
Count the words
Step 2. count_words(word_list) should return a dict from each word to the number of times it occurs, with the words in the order they first appear. words() from step 1 is already there. Fill in the loop.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
Loop over word_list and update counts for each word.
Hint 2
counts.get(word, 0) is the count so far, or 0 for a new word.
Hint 3
for word in word_list: counts[word] = counts.get(word, 0) + 1
Show a solution
One way to solve it. Yours can look different and still pass the checks.
PUNCTUATION = ".,!?;:"
def words(text: str) -> list[str]:
"""Split text into lower-case words without surrounding punctuation."""
cleaned = [word.strip(PUNCTUATION) for word in text.lower().split()]
return [word for word in cleaned if word]
def count_words(word_list: list[str]) -> dict[str, int]:
"""Return how often each word occurs."""
counts: dict[str, int] = {}
for word in word_list:
counts[word] = counts.get(word, 0) + 1
return counts
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
PUNCTUATION = ".,!?;:"
def words(text: str) -> list[str]:
"""Split text into lower-case words without surrounding punctuation."""
cleaned = [word.strip(PUNCTUATION) for word in text.lower().split()]
return [word for word in cleaned if word]
def count_words(word_list: list[str]) -> dict[str, int]:
"""Return how often each word occurs."""
counts: dict[str, int] = {}
# Add 1 to the count of each word; a new word starts at 0.
return counts
test_main.py
from main import count_words, words
def test_counts():
"""Counts each word"""
got = count_words(["the", "cat", "the"])
assert got == {"the": 2, "cat": 1}, f"got {got!r}, expected {{'the': 2, 'cat': 1}}"
def test_first_seen_order():
"""Keeps the words in the order they first appear"""
got = list(count_words(["b", "a", "b"]))
assert got == ["b", "a"], f"the keys are {got!r}, expected ['b', 'a']"
def test_with_words():
"""Works on the output of words()"""
got = count_words(words("Go, go, GO!"))
assert got == {"go": 3}, f"got {got!r}, expected {{'go': 3}}"
def test_empty():
"""No words give an empty dict"""
got = count_words([])
assert got == {}, f"count_words([]) returned {got!r}, expected {{}}"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyExercise 3 of 3
The top words
Step 3. top_words(counts, n) should return the n most frequent (word, count) pairs, highest count first, and words with the same count in alphabetical order. The starter sorts alphabetically only. The program at the bottom prints the top three of a sentence with enumerate().
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
Sort counts.items() with a key function that looks at the count.
Hint 2
A key can return a tuple: Python compares the first part, then the second. A negative count sorts high counts first.
Hint 3
sorted(counts.items(), key=lambda item: (-item[1], item[0]))[:n]
Show a solution
One way to solve it. Yours can look different and still pass the checks.
PUNCTUATION = ".,!?;:"
def words(text: str) -> list[str]:
"""Split text into lower-case words without surrounding punctuation."""
cleaned = [word.strip(PUNCTUATION) for word in text.lower().split()]
return [word for word in cleaned if word]
def count_words(word_list: list[str]) -> dict[str, int]:
"""Return how often each word occurs."""
counts: dict[str, int] = {}
for word in word_list:
counts[word] = counts.get(word, 0) + 1
return counts
def top_words(counts: dict[str, int], n: int) -> list[tuple[str, int]]:
"""Return the n most frequent (word, count) pairs; ties in alphabetical order."""
ranked = sorted(counts.items(), key=lambda item: (-item[1], item[0]))
return ranked[:n]
text = "The cat saw the dog. The dog saw a bird!"
for place, (word, count) in enumerate(top_words(count_words(words(text)), 3), start=1):
print(place, word, count)
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
PUNCTUATION = ".,!?;:"
def words(text: str) -> list[str]:
"""Split text into lower-case words without surrounding punctuation."""
cleaned = [word.strip(PUNCTUATION) for word in text.lower().split()]
return [word for word in cleaned if word]
def count_words(word_list: list[str]) -> dict[str, int]:
"""Return how often each word occurs."""
counts: dict[str, int] = {}
for word in word_list:
counts[word] = counts.get(word, 0) + 1
return counts
def top_words(counts: dict[str, int], n: int) -> list[tuple[str, int]]:
"""Return the n most frequent (word, count) pairs; ties in alphabetical order."""
return sorted(counts.items())[:n]
text = "The cat saw the dog. The dog saw a bird!"
for place, (word, count) in enumerate(top_words(count_words(words(text)), 3), start=1):
print(place, word, count)
test_main.py
from main import top_words
def test_most_frequent_first():
"""Puts the most frequent word first"""
got = top_words({"a": 1, "the": 3, "dog": 2}, 2)
assert got == [("the", 3), ("dog", 2)], f"got {got!r}, expected [('the', 3), ('dog', 2)]"
def test_ties_alphabetical():
"""Words with the same count come in alphabetical order"""
got = top_words({"saw": 2, "dog": 2, "the": 3}, 3)
assert got == [("the", 3), ("dog", 2), ("saw", 2)], f"got {got!r}, expected [('the', 3), ('dog', 2), ('saw', 2)]"
def test_n_larger_than_dict():
"""Returns every pair when n is larger than the number of words"""
got = top_words({"hi": 1}, 5)
assert got == [("hi", 1)], f"got {got!r}, expected [('hi', 1)]"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyCommon mistakes
Reading a missing key with []
stock = {"apple": 3}
print(stock["pear"])
What Python prints
KeyError: 'pear'Why, and the fix
d[key] raises KeyError when the key is not there. Test first with if "pear" in stock, or use stock.get("pear", 0) to get a default.
Deleting keys while looping over the dict
stock = {"apple": 0, "pear": 5}
for fruit in stock:
if stock[fruit] == 0:
del stock[fruit]
What Python prints
RuntimeError: dictionary changed size during iterationWhy, and the fix
Loop over a copy of the keys, for fruit in list(stock), or build a new dict: stock = {k: v for k, v in stock.items() if v != 0}.
Using a list as a key
seats = {["row", 1]: "ana"}
What Python prints
TypeError: cannot use 'list' as a dict key (unhashable type: 'list')Why, and the fix
Dict keys must be hashable, like set items. Use a tuple: {("row", 1): "ana"}.
Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source
Exit ticket
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.