A list answers one question well: what is at position 3? Many programs need a different question answered: what is the price of tea, how many times have I seen this word, has this person already voted? You can answer those with a list and a loop, but Python has two containers built for them. A dictionary finds a value by a key you choose. A set knows whether it holds something. Between them they replace a great many loops.
Before this, you can: build and change a list; loop over a string or a list; count the turns that pass a test.
By the end you will be able to look a value up by its key, add and change entries and deal with a key that is missing, loop over a dictionary, count things with one, use a set to ask "have I seen this?", and choose between a list, a dictionary and a set by the question you need answered.
Before you read on, decide what this does:
counts = {}
for word in ["the", "cat"]:
counts[word] = counts[word] + 1
File "your_program.py", line 3
counts[word] = counts[word] + 1
KeyError: 'the'
It stops on the first word. counts[word] + 1 has to read the count for "the" before it can add to it, and there is no entry for "the" yet. Reading a key that is not there is an error, a KeyError, and the message names the key. Every counting program meets this, on the first new thing it sees. The lesson shows the two usual ways round it.
A dictionary holds entries. Each entry is a key and the value that goes with it, written with a colon between them, inside curly brackets:
prices = {"tea": 2, "cake": 4}
print(prices["cake"])
print(len(prices))
4
2
prices["cake"] looks like indexing, and it is the same idea: something in square brackets picks out a value. But what goes in the brackets is a key, not a position. A dictionary has no position 0:
prices = {"tea": 2, "cake": 4}
print(prices[0])
File "your_program.py", line 2
print(prices[0])
KeyError: 0
Python looked for an entry whose key is the number 0 and found none. Keys are usually text or numbers, and a pair such as (2, 0) can be a key too. A list cannot.
Finding a value by its key is usually fast even in a large dictionary, because Python does not search through the entries one by one.
Assigning to a key adds an entry if the key is new, and replaces the value if the key is already there. There is no separate "add":
prices = {"tea": 2}
prices["cake"] = 4
prices["tea"] = 3
print(prices)
{'tea': 3, 'cake': 4}
A dictionary can change, like a list, and everything in References and Grids applies: two names can refer to one dictionary, and a function can change the one it is given. When you want a second dictionary, ask for one, exactly as with a list: copy makes a new dictionary with the same entries. Adding an entry to the copy, or giving one of its keys a new value, leaves the original's entries as they were. The values themselves are shared, as the rows of a grid were: if a value is a list, both dictionaries refer to that one list.
stock = {"ball": 3, "net": 1}
left = stock.copy()
left["ball"] = 0
print(stock["ball"], left["ball"])
3 0
Reading is where care is needed, because a missing key is an error. There are two ways to avoid it. in asks whether a key is there. get reads a key and hands back a value of your choosing when it is missing:
prices = {"tea": 2}
print("milk" in prices)
print(prices.get("milk", 0))
print(prices.get("tea", 0))
print(len(prices))
False
0
2
1
in on a dictionary asks about keys, not values. And get only reads: after prices.get("milk", 0) there is still no entry for milk, as the last line shows.
A for loop over a dictionary visits its entries in the order they were added. items() hands out each entry as a pair, which the loop takes apart into two names:
prices = {"tea": 2, "cake": 4}
for item, price in prices.items():
print(item, "costs", price)
tea costs 2
cake costs 4
This is the pair from the last module at work: each entry arrives as (key, value), and item, price takes it apart.
from scimigo import canvas, text
canvas(600, 400)
prices = {"tea": 2, "cake": 4, "soup": 5}
y = 60
for item, price in prices.items():
text(60, y, item)
text(300, y, price)
y = y + 40
Looping over the dictionary itself, for item in prices, gives the keys only.
Counting how often each thing appears is the job a dictionary is best known for. The key is the thing, and the value is how many times it has been seen:
counts = {}
for letter in "banana":
counts[letter] = counts.get(letter, 0) + 1
print(counts)
{'b': 1, 'a': 3, 'n': 2}
The line inside the loop is worth reading slowly. counts.get(letter, 0) is the count so far, or 0 if this letter is new. One is added, and the result is stored under the letter, which makes the entry the first time and replaces it after that. This is the first way round the error at the top of the page.
The second way spells the two cases out, with in:
counts = {}
for letter in "banana":
if letter in counts:
counts[letter] = counts[letter] + 1
else:
counts[letter] = 1
print(counts["a"])
3
Both are fine. The get version is shorter, and is the one you will see most.
A set holds values, each one once, with no keys and no positions. set(...) makes one from a list, and curly brackets with no colons write one out:
visitors = ["ana", "bo", "ana", "cy", "bo"]
seen = set(visitors)
print(len(seen))
print("cy" in seen)
seen.add("ana")
seen.add("dee")
print(len(seen))
3
True
4
Five names went in and three are held, because repeats are kept once. Adding "ana" again changes nothing. A set has no order you can rely on, so there is no seen[0].
What a set is for is the question in. Asking whether a value is in a list means looking through the list; a set can usually answer without looking at every value it holds. So the usual pattern is to remember what you have met in a set and consult it as you go:
names = ["ana", "bo", "ana", "cy", "bo"]
seen = set()
first_visits = []
for name in names:
if name not in seen:
first_visits.append(name)
seen.add(name)
print(first_visits)
['ana', 'bo', 'cy']
The list keeps the order; the set answers "have I seen this?". They are often used together like this.
Choose a container by the question you will ask of it most.
| You need to ask | Use | Because |
|---|---|---|
| what is at position 3? what comes next? | a list | it keeps order, and allows repeats |
| what goes with this name? | a dictionary | it finds a value by its key |
| have I seen this? is it one of these? | a set | it answers in without searching, and holds each value once |
Scores in the order they were recorded: a list. The stock of each item in a shop: a dictionary, from item to number. The people who have already voted: a set. A wrong choice still works, and costs you a loop every time you ask.
You will look values up, add and change entries, see what a missing key does and avoid it in two ways, draw a menu from a dictionary, count the letters of a word, use a set to ask what has been seen, choose a container for five small jobs, and then write functions that count words and find the first letter that appears only once.