Python, Concept by Concept

unit

Dictionaries

A club counted these votes: blue, gold, blue, green, blue, gold. Which option won, and by how many votes? A plain list keeps every vote, but it does not give each option a labelled total.

By Lesson 3, you will build a tally from the list and report the winner.

You will learn

  • Make a dictionary that connects each key to a value.
  • Read, add, and update values by key, and use get when a key may be missing.
  • Loop through keys or key and value pairs.
  • Build a count for each choice and find the choice with the largest count.

Lesson 1: Key/Value Maps

Give each value a key

A dictionary stores key/value pairs. Each key is a label that leads to one value. Curly braces make a dictionary, and a colon separates each key from its value. The braces {} make the empty dictionary, which has no key/value pairs yet.

vote_counts = {"blue": 3, "gold": 2, "green": 1}
print(vote_counts)

Output

{'blue': 3, 'gold': 2, 'green': 1}

Notice

Each label has one stored count; the braces hold the pairs.

Read one stored value by putting its key inside square brackets.

print(vote_counts["blue"])

Output

3

Notice

The key "blue" finds its count, 3.

A function can use the same lookup and return the count to its caller.

def blue_vote_count():
    vote_counts = {"blue": 3, "gold": 2, "green": 1}
    return vote_counts["blue"]

print(blue_vote_count())

Output

3

The function returns 3. A dictionary literal can hold lookup information for prices, scores, codes, and tallies.

Membership checks keys

The expression key in d tests whether key is one of dictionary d's keys, not its values. This is different from asking whether a value appears anywhere in the dictionary.

def has_medal(medals, color):
    return color in medals

medal_counts = {"gold": 2, "silver": 4}
print(has_medal(medal_counts, "gold"))
print(has_medal(medal_counts, "bronze"))
print(2 in medal_counts)

Output

True
False
False

The calls print True, False, then False. The value 2 is stored in medal_counts, but 2 in medal_counts is false because membership checks keys.

The missing-key trap

Square-bracket access expects the key to exist. If the dictionary has no "bronze" key, this expression cannot find a value:

medal_counts["bronze"]

Python would report this traceback. It is shown as text so the lesson cell does not raise an error.

Traceback (most recent call last):
  ...
KeyError: 'bronze'

Read the final line first: it names the missing key.

Read safely with get

d.get(key, default) returns the key's value when the key exists. Otherwise it returns the default. It does not add the missing key.

def medal_count(medals, color):
    return medals.get(color, 0)

medal_counts = {"gold": 2, "silver": 4}
print(medal_count(medal_counts, "silver"))
print(medal_count(medal_counts, "bronze"))

Output

4
0

The calls return 4 and 0. Use square brackets when a key must exist. Use get when a sensible default handles a missing key.

Add or update a pair

Assignment through a key stores a value. d[key] = value adds a new pair when the key is missing and updates the old value when the key already exists.

def set_supply(item, amount):
    supplies = {"markers": 6}
    supplies[item] = amount
    return supplies

print(set_supply("paper", 25))
print(set_supply("markers", 10))

Output

{'markers': 6, 'paper': 25}
{'markers': 10}

The first call adds "paper". The second call updates "markers" from 6 to 10. Meaningful names make both the key and the stored value clear.

The next cell shows how to increase a count already stored under a key.

counts = {"blue": 3}
counts["blue"] = counts["blue"] + 1
print(counts)

Output

{'blue': 4}

Notice

The old value is read before the new value, 4, is stored.

A lookup table

A lookup table turns a familiar label into useful information. The function's parameter supplies the key; the return statement sends its value back. Dictionary keys can be numbers, not only strings. In points[level], the integer in level looks up the matching integer key.

def roman_value(symbol):
    values = {"I": 1, "V": 5, "X": 10}
    return values[symbol]

def level_points(level):
    points = {1: 10, 2: 20, 3: 30}
    return points[level]

print(roman_value("I"))
print(roman_value("X"))
print(level_points(2))

Output

1
10
20

A real program can read one Roman symbol and use the lookup table from this lesson.

Try it yourself

symbol = input("Roman symbol (I, V, or X): ")
print(roman_value(symbol))

Notice

The typed symbol becomes the dictionary key; you can try another symbol when you run this cell.

Lesson 2: Loop over a Dictionary

A scoreboard has several labelled values. Loops let one function visit every entry without naming the players one at a time.

Loop through the keys

for key in d visits each key. Use d[key] inside the loop to read that key's value.

def total_using_keys(scores):
    total = 0
    for player in scores:
        total = total + scores[player]
    return total

print(total_using_keys({"Nia": 8, "Omar": 11, "Paz": 6}))

Output

25

The call returns 25. On each loop trip, player holds one key and scores[player] reads its integer score.

A dictionary remembers the order in which new keys were added. Try adding three keys in order.

order = {}
order["zinc"] = 1
order["iron"] = 2
order["neon"] = 3
for key in order:
    print(key)

Output

zinc
iron
neon

Notice

The keys print as zinc, iron, neon, in their first-added order.

Loop through key/value pairs

d.items() provides each key together with its value. The loop header has two loop names, one per pair: the first receives the key, and the second receives the value. This is the dictionary-loop form, not an assignment statement.

def print_scores(scores):
    for player, score in scores.items():
        print(f"{player}: {score}")

print_scores({"Nia": 8, "Omar": 11})

Output

Nia: 8
Omar: 11

The equivalent one-name form is also fine:

for player in scores:
    score = scores[player]
    print(f"{player}: {score}")

Both forms visit the same entries. Use the form that makes the job easiest to read.

Ask for keys or values

d.keys() provides the keys. d.values() provides the values. You can loop over either one directly.

def show_score_parts(scores):
    for player in scores.keys():
        print(player)
    for score in scores.values():
        print(score)

show_score_parts({"Nia": 8, "Omar": 11})

Output

Nia
Omar
8
11

Small built-in helpers

len(d) counts key/value pairs. sum(d.values()) totals numeric values. max(d.values()) returns the largest numeric value. These tools answer questions about values, but they do not tell you which key owns an extreme value.

def score_summary(scores):
    print(f"players: {len(scores)}")
    print(f"total: {sum(scores.values())}")
    return max(scores.values())

print(score_summary({"Nia": 8, "Omar": 11, "Paz": 6}))

Output

players: 3
total: 25
11

Find the key with the largest value

To return the winning key, carry both the best key and best value seen so far. The input below is non-empty and has a unique largest count.

def most_common(counts):
    best_key = ""
    best_count = -1
    for key, count in counts.items():
        if count > best_count:
            best_key = key
            best_count = count
    return best_key

print(most_common({"blue": 3, "gold": 2, "green": 1}))

Output

blue

The strict > comparison replaces the leader only when a larger count appears. For a tie, the first tied key visited wins. A specification should promise a unique maximum or state this first-key tie rule.

A real scoreboard can read a row count followed by that many name score lines, then total the scores.

Try it yourself

n = int(input("How many players? "))
scores = {}
for i in range(n):
    parts = input("Name and score: ").split()
    scores[parts[0]] = int(parts[1])
print(total_using_keys(scores))

Notice

Each row adds a named score before the keys loop computes the total.

Lesson 3: Build Maps from Data

The real power of a dictionary appears when a loop builds the labelled values from raw data.

Build a frequency map

Start with an empty dictionary. For each word, make a lowercase key. If that key already exists, add 1 to its integer count. Otherwise create the key with count 1. This missing-key if/else pattern is the primary tally pattern.

def tally_words(words):
    counts = {}
    for word in words:
        key = word.lower()
        if key in counts:
            counts[key] = counts[key] + 1
        else:
            counts[key] = 1
    return counts

print(tally_words(["Sun", "rain", "sun", "SUN"]))

Output

{'sun': 3, 'rain': 1}

The call returns {'sun': 3, 'rain': 1}. The .lower() call makes differently capitalized versions share one key. Each stored count is an integer.

The same missing-key pattern can count characters in one word, one letter at a time.

letters = {}
for ch in "melon":
    if ch in letters:
        letters[ch] = letters[ch] + 1
    else:
        letters[ch] = 1
print(letters)

Output

{'m': 1, 'e': 1, 'l': 1, 'o': 1, 'n': 1}

Notice

melon has five different letters, so each stored count is 1.

The get shortcut

After you understand the explicit missing-key pattern, get can express the same update in one assignment. A missing key contributes the default count 0 before 1 is added.

def tally_with_get(words):
    counts = {}
    for word in words:
        key = word.lower()
        counts[key] = counts.get(key, 0) + 1
    return counts

print(tally_with_get(["Echo", "echo", "drum"]))

Output

{'echo': 2, 'drum': 1}

Two dictionaries compare equal when they hold the same key/value pairs, even if the pairs were added in a different order.

print(tally_with_get(["a", "b"]) == {"b": 1, "a": 1})

Output

True

Notice

The comparison prints True; pair order does not change dictionary equality.

Group words by first letter

A dictionary value can be a list. Read word[0] to get the first character. Existing groups receive .append(word); a missing group starts with a one-word list.

def group_by_first(words):
    groups = {}
    for word in words:
        letter = word[0]
        if letter in groups:
            groups[letter].append(word)
        else:
            groups[letter] = [word]
    return groups

print(group_by_first(["map", "moon", "sun", "mist"]))

Output

{'m': ['map', 'moon', 'mist'], 's': ['sun']}

The call returns {'m': ['map', 'moon', 'mist'], 's': ['sun']}. Each list keeps its words in input order. For this pattern, every input word must be non-empty.

Use the explicit membership form above. Correct: store [word] in groups[letter] for a new letter, then append to groups[letter] when that letter appears again. Wrong: appending to a fresh list returned by groups.get(letter, []) when the letter is missing changes that separate list but never stores the new key in groups.

Make a sorted leaderboard

A list may hold other lists, as in Unit 10's grids; each two-item inner list is one row. When .sort() compares two rows, it compares their first items first, so the scores decide the order. The second item only breaks ties, which the samples in this lesson avoid.

def leaderboard(scores):
    board = []
    for name, score in scores.items():
        board.append([score, name])
    board.sort()
    return board

print(leaderboard({"Ari": 12, "Bea": 7, "Chen": 15}))

Output

[[7, 'Bea'], [12, 'Ari'], [15, 'Chen']]

The returned leaderboard is [[7, 'Bea'], [12, 'Ari'], [15, 'Chen']]. The loop builds the list one row at a time — each row is added with append.

Filter keys into a list

Sometimes the result should contain only keys whose values pass a test. Start an empty list, test each pair, then append a passing key.

def keys_at_least(scores, minimum):
    passing = []
    for name, score in scores.items():
        if score >= minimum:
            passing.append(name)
    return passing

print(keys_at_least({"Ari": 12, "Bea": 7, "Chen": 15}, 10))

Output

['Ari', 'Chen']

Read records

A record line can carry a name and a number. Split it, convert the number, then store the pair.

scores = {}
record = "Rin 12"
parts = record.split()
scores[parts[0]] = int(parts[1])
print(scores)

Output

{'Rin': 12}

Notice

The first piece becomes a key, and the converted second piece becomes its integer value.

For word input, split one typed line and update a tally for each word.

Try it yourself

words = input("Words: ").split()
print(tally_words(words))

Notice

One input line can supply several words to the tally.

For record input, read a row count and then that many name score lines.

Try it yourself

n = int(input("How many records? "))
scores = {}
for i in range(n):
    parts = input("Name and score: ").split()
    scores[parts[0]] = int(parts[1])
print(scores)

Notice

Each record becomes one dictionary pair; repeated names replace the earlier score in this example.

Put it together: report the vote winner

The opener's raw votes can now become a frequency map. A second loop finds the unique largest count, and an f-string reports the result.

def vote_report(votes):
    # Build one integer count for each lowercase choice.
    counts = {}
    for vote in votes:
        choice = vote.lower()
        if choice in counts:
            counts[choice] = counts[choice] + 1
        else:
            counts[choice] = 1

    winner = ""
    winner_count = -1
    for choice, count in counts.items():
        if count > winner_count:
            winner = choice
            winner_count = count
    return f"{winner} wins with {winner_count} votes"

print(vote_report(["blue", "gold", "blue", "green", "blue", "gold"]))

Output

blue wins with 3 votes

Visible payoff: the call returns "blue wins with 3 votes". The function uses meaningful names for each job: raw votes, built counts, current choice, and final winner. Short comments can mark a major stage without repeating every line.

Recap

  • {'blue': 3} stores a value under the key 'blue'.
  • counts[key] reads a known key; counts.get(key, 0) supplies zero for a missing key.
  • key in counts checks keys, and a loop can visit every key or key and value pair.
  • A counting loop builds a frequency map one vote at a time.
  • Another loop can track the key with the largest value.

You can now count the club's votes and report the winning choice.

Go to the exercises (28)

For parents and teachers: report a problem