Course outline · 0% complete

0/32 lessons0%

Course overview →

Dictionaries: Looking Things Up by Name

lesson 7-1 · ~12 min · 21/32

Recall from lesson 6-2 that the method adding an item to the end of a list is lst.append(x).

It is the workhorse of the build pattern, extending the list in place so each new result lands after the previous one.

Worth noticing what append implies about lists in general: items are found by position number, and their positions are decided by the order in which they arrived. This lesson introduces a container that finds items by name instead, which changes how lookups are written and what they cost.

A dict maps keys to values

Almost every record a real program touches is key-value shaped: a user profile with a name, an email, and a plan, a settings file, or the JSON that every web service sends and receives. The dictionary is Python's container for that shape, and after the list it is the type you will reach for most often.

A list answers what is at position 2? A dictionary answers what is Ada's age? It stores key: value pairs inside curly braces.

ages = {"Ada": 36, "Grace": 45}
OperationExampleResult
look upages["Ada"]36
add or replaceages["Alan"] = 41new pair stored
key exists?"Grace" in agesTrue
safe look upages.get("Linus", 0)0, no crash
sizelen(ages)number of pairs

Keys must be unique, so assigning to an existing key replaces its value rather than adding a second entry. That is what makes a dict the natural home for anything identified by a name or an id.

Looking up a missing key with brackets stops the program with a KeyError. Using .get(key, default) returns the default instead, and that survivable behavior is what powers the counting pattern coming up at the end of this lesson.

Each dict operation in turn

The lines below follow the table above, and the final .get deliberately asks for a key that is not present.

ages = {"Ada": 36, "Grace": 45}
print(ages["Ada"])
ages["Alan"] = 41
print(len(ages))
print("Grace" in ages)
print(ages.get("Linus", 0))

Output

36
3
True
0

Bracket lookup returned 36 without any searching through positions, since a dict goes straight to the key. The assignment added a third pair, which len confirms by reporting 3 rather than 2. The in test asks about keys rather than values, so it answers True for "Grace" and would answer False for 45. The last line survives a missing key because .get was given a fallback, and it returns that 0 instead of raising an error.

With ages holding {"Ada": 36}, running ages["Linus"] stops the program with a KeyError.

Bracket lookup insists that the key already exist, and it raises rather than inventing a value. Two other forms cover the cases where that is not what you want. ages.get("Linus", 0) returns the supplied default and keeps running, while ages["Linus"] = 41 creates the entry outright.

The distinction to hold onto is that reading never creates. Only assignment adds a key, which is why a typo in a lookup produces a loud KeyError instead of quietly introducing a misspelled entry.

"Ada""Grace""Alan"364541keysvalues
A dictionary is a set of arrows from unique keys to their values. Lookup follows the arrow.

The counting pattern from lesson 5-3 becomes far more capable with a dict, because one accumulator can now track a separate count for every distinct item.

word = "banana"
counts = {}
for ch in word:
    counts[ch] = counts.get(ch, 0) + 1
print(counts)

Output

{'b': 1, 'a': 3, 'n': 2}

The single line inside the loop does the whole job. counts.get(ch, 0) reads the current count for this character, supplying 0 the first time it is seen, and the assignment stores one more under that key. That default is what removes the need for an if checking whether the key exists yet.

The print order is worth noting: dicts remember the order keys were first inserted, so b appears first because it was the first character encountered, not because of any alphabetical rule.