Real-World Use Cases
Concepts covered: Frequency Counting
Most real programs use dictionaries for 2 jobs: holding settings and counting things.
Settings with sensible defaults
config = {"theme": "dark", "font_size": 14}
theme = config.get("theme", "light")
font = config.get("font_size", 12)
lang = config.get("language", "en")
print(theme, font, lang)Output:
dark 14 en
A config dictionary holds the settings a user or a deployment changed. Anything they left alone is absent, and .get with a default fills it in: "language" was never set, so the program runs in "en". The defaults live in the code at the point of use, the overrides live in the dictionary, and neither side needs to know which keys the other has.
| Setting | In config | Default | Value used |
|---|---|---|---|
| theme | yes | light | dark |
| font_size | yes | 12 | 14 |
| language | no | en | en |
Counting with a dictionary
To count how often each word appears in a list, you need 1 counter per distinct word, and you do not know the words in advance. A dictionary with the word as key and the count as value grows a counter the first time each word shows up:
# Count word frequencies
word_counts = {}
words = ["apple", "banana", "apple", "cherry", "apple", "banana"]
for word in words:
if word in word_counts:
word_counts[word] = word_counts[word] + 1
else:
word_counts[word] = 1
print(word_counts){'apple': 3, 'banana': 2, 'cherry': 1}
| Word | Already a key | Action | Dictionary after |
|---|---|---|---|
| apple | no | create apple: 1 | {'apple': 1} |
| banana | no | create banana: 1 | {'apple': 1, 'banana': 1} |
| apple | yes | 1 + 1 = 2 | {'apple': 2, 'banana': 1} |
| cherry | no | create cherry: 1 | {'apple': 2, 'banana': 1, 'cherry': 1} |
| apple | yes | 2 + 1 = 3 | {'apple': 3, 'banana': 1, 'cherry': 1} |
| banana | yes | 1 + 1 = 2 | {'apple': 3, 'banana': 2, 'cherry': 1} |
Depending on the membership test, each word gets one of 2 bracket writes: create the pair with a value of 1, or replace the current count with that count plus 1. After 6 words the dictionary holds 3 pairs, apple at 3, banana at 2, cherry at 1, in the order each word first appeared. The same loop counts anything that can be a key: status codes, letter grades, product ids.
scores = [92, 67, 85, 74, 59, 88]
grades = {}
for score in scores:
if score > 84:
grade = "A"
elif score > 69:
grade = "B"
else:
grade = "C"
if grade in grades:
grades[grade] = grades[grade] + 1
else:
grades[grade] = 1
print(grades)Output:
{'A': 3, 'C': 2, 'B': 1}Here the key is computed before the count. Each score becomes a letter through the if/elif/else chain, and the tally code underneath never changes. "A" enters the dictionary first because 92 is the first score, then "C" for 67, then "B" for 74, so that is the printed order even though B sits between A and C alphabetically.
Lookup tables
status_text = {200: "OK", 404: "Not Found", 500: "Server Error"}
codes = [200, 404, 418, 200]
for code in codes:
print(code, status_text.get(code, "Unknown"))Output:
200 OK 404 Not Found 418 Unknown 200 OK
A dictionary that translates one value into another is a lookup table. Code 418 has no entry, so .get supplies "Unknown" and the loop keeps going; a bracket lookup would have crashed on the third code. Lookup tables replace long if/elif chains: supporting a new status means adding 1 pair, with no new branch to write or test.
Capstone: a tiny inventory
This program starts from a stocked dictionary and runs each order through a membership test before touching the counts with bracket reads and writes. One removal comes at the end. Predict every printed line before you reveal the output.
stock = {"apple": 4, "pear": 0, "plum": 7}
orders = ["apple", "fig", "pear", "apple"]
for item in orders:
if item not in stock:
print(item, "not stocked")
elif stock[item] == 0:
print(item, "sold out")
else:
stock[item] = stock[item] - 1
print(item, "shipped, left:", stock[item])
del stock["pear"]
print(stock)Output:
apple shipped, left: 3
fig not stocked
pear sold out
apple shipped, left: 2
{'apple': 2, 'plum': 7}Order by order: "fig" fails the not in test and prints not stocked, with no bracket read ever attempted on it. "pear" is a key but its count is 0, so the elif catches it. Each "apple" order writes back the count minus 1, so the count goes from 4 to 3 to 2. The final del drops the sold-out pear, and the printed dictionary shows the 2 remaining products with their updated counts.
Practice
What does the final line print?
votes = ["red", "blue", "red", "green", "red"]
tally = {}
for color in votes:
if color in tally:
tally[color] = tally[color] + 1
else:
tally[color] = 1
print(tally["red"], tally.get("blue", 0), tally.get("pink", 0))- 2 1 0
- 3 1 0
- 3 2 0
- A traceback at the last get, because pink was never voted for
Two dictionaries often work as a pair: one counts what happened, the other holds settings that judge the counts. Here a limits config caps how many of each item an order may hold, and any item without its own entry gets a default limit of 1:
counts = {"tea": 4, "jam": 1}
limits = {"tea": 3}
for item in ["tea", "jam"]:
limit = limits.get(item, 1)
if counts[item] > limit:
print(item, "over limit:", counts[item], "of", limit)
else:
print(item, "within limit:", counts[item], "of", limit)Output:
tea over limit: 4 of 3 jam within limit: 1 of 1
"tea" is over its own limit of 3 with a count of 4. "jam" has no entry in limits, so .get supplies 1, and a count of 1 is not greater than 1. The counts are read with brackets because the loop only asks for items it knows were counted; the limits are read with .get because most items have no entry of their own.
About this lesson
This section is part of the Dictionaries: Beginner lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.