Dictionaries
A list finds items by position: scores[0], scores[1]. Often it's much more natural to find things by name: a word's definition, a user's email, a product's price. A dictionary stores key-value pairs, so you look up a value using its key.
Creating a dictionary
Write key: value pairs inside curly braces:
person = {
"name": "Ada",
"age": 36,
"city": "London",
}
print(person)
print(type(person))
print(len(person)) # number of pairsEach pair is a key, a colon, then its value, and the pairs are separated by commas. Keys are usually strings, but they can be numbers or tuples too. Values can be anything, including lists or other dictionaries.
A few rules to know:
- Each key appears only once. If you write the same key twice, the last value wins.
- Keys are case-sensitive:
"name"and"Name"are different keys. - The pairs stay in the order you added them.
{}is an empty dictionary.
Looking up values
Put the key in square brackets:
person = {"name": "Ada", "age": 36, "city": "London"}
print(person["name"])
print(person["age"])Lookups only work in one direction: from a key to its value. There's no person["Ada"] to find the key from a value. That's why you choose the thing you'll search by as the key.
Asking for a key that doesn't exist raises a KeyError:
person = {"name": "Ada", "age": 36}
print(person["email"])Safer lookups with get()
get() returns None instead of crashing, or a default value that you choose:
person = {"name": "Ada", "age": 36}
print(person.get("name"))
print(person.get("email"))
print(person.get("email", "no email"))Use in to check whether a key exists:
person = {"name": "Ada", "age": 36}
print("age" in person)
print("email" in person)in checks the keys, not the values: "Ada" in person is False.
| You write | If the key exists | If it's missing |
|---|---|---|
person["email"] |
its value | KeyError |
person.get("email") |
its value | None |
person.get("email", "no email") |
its value | "no email" |
"email" in person |
True |
False |
None of these add the key to the dictionary; they only read it.
Adding and changing entries
Assigning to a key adds it if it's new, or replaces its value if it already exists:
stock = {"apples": 10, "pears": 4}
# new key: added
stock["bananas"] = 7
# existing key: replaced
stock["apples"] = 12
# add 1 to the current value
stock["pears"] += 1
print(stock)The same syntax adds or updates, so there's no separate "add" method. += needs a current value, though: stock["kiwis"] += 1 is a KeyError because there are no kiwis yet. That's the problem the counting pattern below solves.
update() adds or changes several entries at once:
stock = {"apples": 10}
stock.update({"pears": 3, "apples": 15})
print(stock)Going deeper: Merging dictionaries optional
update() changes a dictionary in place. To build a new merged dictionary instead, use the | operator. When both sides have the same key, the value on the right wins:
defaults = {"theme": "light", "font_size": 14}
choices = {"font_size": 18}
settings = defaults | choices
print(settings)
print(defaults) # unchangedYou may also see the older spelling {**defaults, **choices}, which does the same thing.
Removing entries
stock = {"apples": 10, "pears": 4, "bananas": 7, "kiwis": 2}
# remove a key and give back its value
removed = stock.pop("pears")
print(removed, stock)
# remove a key
del stock["bananas"]
print(stock)
# remove everything
stock.clear()
print(stock)| Use | Removes | Missing key |
|---|---|---|
d.pop(key) |
that key, and gives back its value | KeyError |
d.pop(key, default) |
that key, and gives back its value | gives back default, no error |
del d[key] |
that key | KeyError |
d.clear() |
every key | (never fails) |
Keys, values and items
Three methods give you views of the dictionary's contents:
prices = {"coffee": 3.5, "tea": 2.8, "juice": 4.0}
print(list(prices.keys()))
print(list(prices.values()))
print(list(prices.items()))keys()gives the keys,values()the values, anditems()each pair as a(key, value)tuple.- They're wrapped in
list()here only so they print neatly; on their own they print asdict_keys([...])and so on. - They follow the dictionary's order, so the first key, first value and first item all belong together.
These come in handy for calculations:
prices = {"coffee": 3.5, "tea": 2.8, "juice": 4.0}
print(sum(prices.values()))
print(max(prices.values()))
# a dictionary on its own means its keys
print(sorted(prices))max(prices.values()) gives the highest price, 4.0, not which drink it is. When you pass the dictionary itself, as in sorted(prices), Python uses the keys.
You'll loop over these in the For Loops lesson, which is where dictionaries really shine.
Counting with a dictionary
A very common pattern is counting things. get(key, 0) gives 0 the first time a key is seen:
votes = {}
votes["red"] = votes.get("red", 0) + 1
votes["blue"] = votes.get("blue", 0) + 1
votes["red"] = votes.get("red", 0) + 1
print(votes)| Line | get() gives |
Stored | votes afterwards |
|---|---|---|---|
| 1st (red) | 0 (not there yet) |
0 + 1 = 1 |
{'red': 1} |
| 2nd (blue) | 0 (not there yet) |
0 + 1 = 1 |
{'red': 1, 'blue': 1} |
| 3rd (red) | 1 |
1 + 1 = 2 |
{'red': 2, 'blue': 1} |
The same line works whether or not the key exists yet, which is why this pattern is everywhere.
Going deeper: Counting the easy way with Counter optional
Counting is so common that Python has a ready-made tool for it. Counter from the collections module counts everything in a list in one step:
from collections import Counter votes = ["red", "blue", "red", "green", "red", "blue"] counts = Counter(votes) print(counts) print(counts["red"]) # missing keys count as 0 print(counts["purple"]) # the two most common print(counts.most_common(2))
A Counter is a dictionary underneath, so everything from this lesson still works on it.
Going deeper: Dictionaries inside dictionaries optional
Values can be lists or other dictionaries, which lets you model structured data:
student = {
"name": "Mia",
"grades": [88, 92, 79],
"address": {
"city": "Leeds",
"postcode": "LS1 4AP",
},
}
print(student["grades"][1])
print(student["address"]["city"])
student["grades"].append(95)
print(student["grades"])This is exactly the shape of JSON, the format most web APIs use to send data.
Going deeper: What can be a key? optional
Keys must be immutable: strings, numbers and tuples work, but lists don't:
distances = {("London", "Paris"): 344}
print(distances[("London", "Paris")])
bad = {["a", "b"]: 1}Exercises
Exercise 1: Contact card
Create a dictionary for a contact with keys name, phone and email, then print the phone number.
contact = {}
Write key: value pairs inside the curly braces, separated by commas.
Read the phone number with contact["phone"].
Exercise 2: Update the inventory
Sell 3 apples, add a new item "mangos" with 6 in stock, and remove "pears". It should print {'apples': 7, 'bananas': 5, 'mangos': 6}.
inventory = {"apples": 10, "pears": 4, "bananas": 5}
print(inventory)inventory["apples"] -= 3 sells three apples.
Add a new key by assigning to it, and remove one with del inventory["pears"].
Exercise 3: Safe lookup
Look up the capital of country. If the country isn't in the dictionary, print Unknown instead of crashing.
capitals = {"France": "Paris", "Japan": "Tokyo", "Kenya": "Nairobi"}
country = "Brazil"
get() takes a second argument: the value to return when the key is missing.
Exercise 4: Order total
The order dictionary maps items to quantities, and prices maps items to prices. Calculate the total for coffee and cake and print Total: 12.0.
prices = {"coffee": 3.0, "cake": 4.5, "tea": 2.5}
order = {"coffee": 1, "cake": 2}
Multiply each item's price by its quantity, like prices["coffee"] * order["coffee"].
Add the coffee and cake amounts together, then print them after "Total:".
Dictionary methods at a glance
Each example starts from d = {"a": 1, "b": 2}. The Result column shows what the method gives back, or what d looks like afterwards for methods that change it.
| Method | What it does | Example | Result |
|---|---|---|---|
get(key, default) |
Value for key, or default (None if not given) when missing |
d.get("c", 0) |
0 |
keys() |
All the keys | list(d.keys()) |
['a', 'b'] |
values() |
All the values | list(d.values()) |
[1, 2] |
items() |
All the pairs, as (key, value) tuples |
list(d.items()) |
[('a', 1), ('b', 2)] |
update(other) |
Adds or replaces entries from another dictionary | d.update({"b": 5, "c": 3}) |
{'a': 1, 'b': 5, 'c': 3} |
pop(key, default) |
Removes key and returns its value (KeyError if missing and no default) |
d.pop("a") |
returns 1, leaves {'b': 2} |
popitem() |
Removes and returns the most recently added pair | d.popitem() |
returns ('b', 2), leaves {'a': 1} |
setdefault(key, value) |
Returns the value for key; if it's missing, adds it with value first |
d.setdefault("c", 0) |
returns 0, leaves {'a': 1, 'b': 2, 'c': 0} |
copy() |
Returns a new (shallow) copy | d.copy() |
{'a': 1, 'b': 2} |
clear() |
Removes every entry | d.clear() |
{} |
dict.fromkeys(keys, value) |
A new dictionary with every key set to value |
dict.fromkeys(["x", "y"], 0) |
{'x': 0, 'y': 0} |
Useful operations that aren't methods:
| Operation | What it does | Example | Result |
|---|---|---|---|
d[key] |
Value for key (KeyError if missing) |
d["b"] |
2 |
d[key] = value |
Adds or replaces an entry | d["c"] = 3 |
{'a': 1, 'b': 2, 'c': 3} |
del d[key] |
Removes an entry | del d["a"] |
{'b': 2} |
key in d |
Is key in the dictionary? |
"a" in d |
True |
len(d) |
Number of entries | len(d) |
2 |
d | other |
A new merged dictionary | d | {"c": 3} |
{'a': 1, 'b': 2, 'c': 3} |
Summary
- A dictionary maps keys to values:
{"name": "Ada"}. - Look up values with
d[key]. A missing key raisesKeyError. Used.get(key, default)to be safe. d[key] = valueadds or updates.pop()anddelremove.keys(),values()anditems()give you the contents.
Know something worth teaching? Write your own tutorial with runnable examples and share it.