CollectionsChapter 37 of 114
Dictionaries
Look values up by a key instead of a position.
Keys and values
person = {"name": "Ada", "born": 1815}
print(person["name"])
print(len(person))Output
Ada 2
A dictionary maps a key to a value. Where a list answers "what is at position 2", a dictionary answers "what is stored under name".
Reading safely
A missing key raises KeyError:
person = {"name": "Ada"}
try:
print(person["age"])
except KeyError as problem:
print("KeyError:", problem)Output
KeyError: 'age'
get() returns None instead, or whatever default you pass:
person = {"name": "Ada"}
print(person.get("age"))
print(person.get("age", "unknown"))
print("age" in person)Output
None unknown False
Use [] when a missing key is a bug, and get() when it is expected.
Adding and changing
Assigning to a key sets it, whether or not it was there:
person = {"name": "Ada"}
person["born"] = 1815
person["name"] = "Ada Lovelace"
print(person)Output
{'name': 'Ada Lovelace', 'born': 1815}Removing
person = {"name": "Ada", "born": 1815, "scratch": True}
del person["scratch"]
print(person.pop("born"))
print(person)
print(person.pop("missing", "not there"))Output
1815
{'name': 'Ada'}
not thereLooping
Looping over a dictionary gives the keys. items() gives both, and is what you usually want:
person = {"name": "Ada", "born": 1815}
for key in person:
print(key)
for key, value in person.items():
print(key, "=", value)Output
name born name = Ada born = 1815
keys() and values() do what they say:
person = {"name": "Ada", "born": 1815}
print(list(person.keys()))
print(list(person.values()))Output
['name', 'born'] ['Ada', 1815]
Order is insertion order
Since Python 3.7 a dictionary keeps keys in the order they were added. That is a language guarantee now, not an accident:
scores = {}
scores["c"] = 3
scores["a"] = 1
print(list(scores))Output
['c', 'a']
Keys must be hashable
Strings, numbers and tuples work. Lists do not:
try:
{[1]: "nope"}
except TypeError:
print("a list cannot be a key")Output
a list cannot be a key
Counting things
The pattern that comes up most often, and the tool that does it for you:
from collections import Counter
words = ["a", "b", "a", "c", "a"]
print(Counter(words).most_common(2))
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)Output
[('a', 3), ('b', 1)]
{'a': 3, 'b': 1, 'c': 1}Merging
defaults = {"colour": "black", "size": 10}
custom = {"size": 12}
print(defaults | custom)
print({**defaults, **custom})Output
{'colour': 'black', 'size': 12}
{'colour': 'black', 'size': 12}The right-hand side wins on a clash, which is exactly what you want for overriding defaults.
Building one
words = ["ada", "grace"]
print({word: len(word) for word in words})
print(dict(zip(["a", "b"], [1, 2])))Output
{'ada': 3, 'grace': 5}
{'a': 1, 'b': 2}Test yourself
3 questionsWhat does person.get("age") give when there is no age key?
Show the answer
None — Use [] when a missing key is a bug, and get() when it is expected.
What order does looping over a dictionary give?
Show the answer
The order the keys were added — Insertion order has been a language guarantee since Python 3.7.
What does defaults | custom give when both have a size key?
Show the answer
The value from custom — The right-hand side wins, which is what you want for overriding defaults.
Nested Dictionaries
Dictionaries inside dictionaries, and how to read them without crashing.