Dictionaries and Sets Cheatsheet
A dictionary stores key:
value pairs and looks up each value by its key. A
set holds distinct items in no particular order. This
sheet collects the Dictionaries and Records and Sets lectures
in one place. Every example can be pasted into a file and run. The file
examples expect the data folder from data.zip next to your program. The Data Files page shows what is in each
file.
Creating a Dictionary
sales = {"Mon": 50, "Tue": 75, "Wed": 60, "Thu": 90, "Fri": 40}
empty = {}
print(sales)
print(len(sales))
print(len(empty)){'Mon': 50, 'Tue': 75, 'Wed': 60, 'Thu': 90, 'Fri': 40}
5
0
Write each pair as key: value, with commas between the
pairs and braces around them all. len gives the number of
pairs, and {} is an empty dictionary. A dictionary keeps
its pairs in the order they were added.
| List | Dictionary | |
|---|---|---|
| Create | scores = [90, 85] |
sales = {"Mon": 50, "Tue": 75} |
| Look up by | index: scores[0] |
key: sales["Mon"] |
| Missing | IndexError |
KeyError |
| Empty | [] |
{} |
| Add | scores.append(77) |
sales["Wed"] = 60 |
in looks |
at the items | at the keys only |
Looking Up a Value
sales = {"Mon": 50, "Tue": 75, "Wed": 60}
print(sales["Wed"])
print("Wed" in sales)
print("Sat" in sales)
print(60 in sales)
print(sales["Sat"])60
True
False
False
KeyError: 'Sat'
d[key] gives back the value for key, and a
missing key raises a KeyError. in and
not in look at the keys only, so 60 in sales
is False. Check with in before a lookup that
might miss:
sales = {"Mon": 50, "Tue": 75, "Wed": 60}
day = input("Day: ")
if day in sales:
print(sales[day])
else:
print("No sales recorded.")Day: Sat
No sales recorded.
Catching a KeyError
sales = {"Mon": 50, "Tue": 75, "Wed": 60}
try:
print(sales["Sat"])
except KeyError as error:
print(f"Missing key: {error}")Missing key: 'Sat'
except KeyError catches the missing key, and
as error stores the exception in error.
Printing error shows the key that was missing, in quotes.
Python programmers use both styles: check with in first, or
try the lookup and catch the KeyError.
To print the whole crash report instead, use
traceback.print_exc():
import traceback
sales = {"Mon": 50, "Tue": 75, "Wed": 60}
try:
print(sales["Sat"])
except KeyError:
traceback.print_exc()
print("Still running.")Traceback (most recent call last):
File "/Users/you/sales.py", line 6, in <module>
print(sales["Sat"])
~~~~~^^^^^^^
KeyError: 'Sat'
Still running.
print_exc prints the report Python prints for a crash,
with the file and line number, and then the program keeps running. It
reads the exception being handled, so except KeyError:
needs no as error. Python 3.10 and older leave out the
~~~~~^^^^^^^ line. The report goes to the error stream, not
to standard output, so when output is sent to a file instead of a
terminal, the report can land before earlier print
lines.
get
sales = {"Mon": 50, "Tue": 75, "Wed": 60}
print(sales.get("Wed"))
print(sales.get("Sat"))
print(sales.get("Sat", 0))60
None
0
d.get(key, default) gives back the value for
key, or default when the key is missing. It
never raises a KeyError. With no default, a missing key
gives back None.
Adding and Replacing
d = {"a": 1}
d["a"] = 5
d["b"] = 2
print(d){'a': 5, 'b': 2}
Assigning to a key that exists replaces its value, and the key keeps its place. Assigning to a new key adds a pair at the end. A dictionary never holds the same key twice.
Starting from {}, each assignment adds a pair. A value
can be any type, even a list:
player = {}
player["name"] = "Ada"
player["pos"] = [3, 4]
player["health"] = 12
print(player){'name': 'Ada', 'pos': [3, 4], 'health': 12}
Keys Typed by the User
"Coffee ", "coffee", and
"COFFEE" are three different keys. Clean typed input before
the lookup: .strip().lower() for keys like
"coffee", or .strip().upper() for keys like
"NY".
menu = {"coffee": 3.00, "tea": 2.50, "bagel": 2.75}
item = input("Item: ").strip().lower()
if item in menu:
print(f"${menu[item]:.2f}")
else:
print("Not on the menu.")Item: Coffee
$3.00
Keys Read from a File
Every field that split gives back is a string, and the
string "1" and the number 1 are different
keys. Convert the field to match the keys:
days = {1: "Monday", 2: "Tuesday"}
text = "1"
print(text in days)
print(int(text) in days)False
True
Lookup Tables
days = {
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday",
7: "Sunday",
}
day = int(input("Day (1-7): "))
if day in days:
print(days[day])
else:
print("Not a valid day")Day (1-7): 3
Wednesday
A lookup table is a dictionary that turns one value
into another. When every case of a match or an
if/elif chain only picks a value, a lookup
table replaces it. When the cases run different code, keep the
match.
Looping Over a Dictionary
sales = {"Mon": 50, "Tue": 75, "Wed": 60}
for day in sales:
print(day, sales[day])
for amount in sales.values():
print(amount)
for day, amount in sales.items():
print(f"{day}: {amount}")Mon 50
Tue 75
Wed 60
50
75
60
Mon: 50
Tue: 75
Wed: 60
| Loop over | Each time around, the loop gets |
|---|---|
d or d.keys() |
a key |
d.values() |
a value |
d.items() |
a (key, value) tuple, unpacked as
key, value |
sum(sales.values()) adds up the values.
sum(sales) tries to add up the keys and raises a
TypeError.
In Key Order: sorted
stock = {"pears": 2, "apples": 5, "kiwis": 1}
print(sorted(stock))
for item in sorted(stock):
print(item, stock[item])['apples', 'kiwis', 'pears']
apples 5
kiwis 1
pears 2
sorted(d) gives back a new list of the keys in order:
alphabetical for strings, smallest first for numbers. The dictionary
keeps its own order.
Largest Value
sales = {"Mon": 50, "Tue": 75, "Wed": 60, "Thu": 90, "Fri": 40}
best_day = ""
best_amount = 0
for day, amount in sales.items():
if amount > best_amount:
best_day = day
best_amount = amount
print(f"Best day: {best_day} ({best_amount})")Best day: Thu (90)
The loop remembers two things as it goes: the best key so far and its
value. Starting best_amount at 0 works when every value is
above 0. max(sales.values()) gives 90, but not
the day that goes with it.
Counting
votes = ["red", "blue", "red", "green", "red"]
counts = {}
for vote in votes:
if vote in counts:
counts[vote] += 1
else:
counts[vote] = 1
print(counts){'red': 3, 'blue': 1, 'green': 1}
A new key starts at 1, and a key seen before goes up by 1.
counts[vote] += 1 alone raises KeyError: 'red'
on the first "red", because += needs an old
value to add to.
get with a default of 0 does the same in one line:
votes = ["red", "blue", "red", "green", "red"]
counts = {}
for vote in votes:
counts[vote] = counts.get(vote, 0) + 1
print(counts){'red': 3, 'blue': 1, 'green': 1}
Totals by Key
The same pattern adds up an amount per key in place of counting by 1.
This program totals price * quantity for each product in
sales.csv:
import os
totals = {}
path = os.path.join("data", "sales.csv")
with open(path, "r") as f:
f.readline()
for line in f:
fields = line.strip().split(",")
product = fields[0]
amount = float(fields[2]) * int(fields[3])
if product in totals:
totals[product] += amount
else:
totals[product] = amount
for product, total in totals.items():
print(f"{product}: ${total:.2f}")P-01: $99.10
P-02: $52.80
Removing a Pair: del
sales = {"Mon": 50, "Tue": 75, "Wed": 60}
del sales["Tue"]
print(sales)
day = "Sat"
if day in sales:
del sales[day]
else:
print(f"No {day} to remove."){'Mon': 50, 'Wed': 60}
No Sat to remove.
del d[key] removes the pair for key. A
missing key raises a KeyError, so the second
del checks with in first.
Combining: update
prices = {"coffee": 3.00, "tea": 2.50}
new_prices = {"tea": 2.75, "bagel": 2.25}
prices.update(new_prices)
print(prices)
print(new_prices){'coffee': 3.0, 'tea': 2.75, 'bagel': 2.25}
{'tea': 2.75, 'bagel': 2.25}
a.update(b) copies every pair of b into
a. A key a already has takes the new value and
keeps its place, and a new key goes at the end. b stays the
same.
Changing While Looping
A loop over a dictionary cannot add or remove keys:
stock = {"apples": 9, "pears": 0, "plums": 4, "kiwis": 0}
for item in stock:
if stock[item] == 0:
del stock[item]RuntimeError: dictionary changed size during iteration
There are two ways around it. One builds a new dictionary, as with lists:
stock = {"apples": 9, "pears": 0, "plums": 4, "kiwis": 0}
in_stock = {}
for item, count in stock.items():
if count > 0:
in_stock[item] = count
print(in_stock){'apples': 9, 'plums': 4}
The other collects the keys in a list, then deletes them after the loop ends:
stock = {"apples": 9, "pears": 0, "plums": 4, "kiwis": 0}
to_delete = []
for item, count in stock.items():
if count == 0:
to_delete.append(item)
for item in to_delete:
del stock[item]
print(stock){'apples': 9, 'plums': 4}
The first leaves stock as it was. The second changes
stock itself.
Changing the value of a key that exists is fine inside the loop,
since the number of keys stays the same:
stock[item] += 1.
Records: Dictionaries of Dictionaries
students = {
"s01": {"name": "Ana", "major": "CS", "gpa": 3.6},
"s02": {"name": "Ben", "major": "Math", "gpa": 3.2},
}
print(students["s01"]["gpa"])
students["s02"]["gpa"] = 3.4
for sid, student in students.items():
print(sid, student["name"], student["gpa"])3.6
s01 Ana 3.6
s02 Ben 3.4
A record is a dictionary whose keys name its fields,
like "name" and "gpa".
students["s01"]["gpa"] reads left to right: the record for
s01, then its gpa field. Inside the loop,
student is one record, so its fields are
student["name"] and student["gpa"].
students["name"] raises KeyError: 'name',
because the keys of students are ids. The name of one
student is students[sid]["name"].
Records from a File
import os
products = {}
path = os.path.join("data", "products.csv")
with open(path, "r") as f:
f.readline()
for line in f:
fields = line.strip().split(",")
products[fields[0]] = {"division": fields[1], "price": float(fields[2])}
print(products["P-010"])
for product_id, product in products.items():
if product["division"] == "divb":
price = product["price"]
print(f"{product_id}: ${price:.2f}"){'division': 'divb', 'price': 10.25}
P-010: $10.25
P-010x: $10.75
Q-011: $11.50
Q-011x: $11.95
Each line of products.csv becomes one record, keyed by
its product id. Converting price once, as the record is
built, means every later use gets a float.
Dictionaries of Lists
scores = {"Ana": [90, 85], "Ben": [70, 88]}
scores["Ana"].append(95)
print(scores)
for name, marks in scores.items():
print(name, sum(marks) / len(marks)){'Ana': [90, 85, 95], 'Ben': [70, 88]}
Ana 90.0
Ben 79.0
scores["Ana"] is a list, so every list tool works on it.
sum(scores) would add up the keys, which are names, and
raise
TypeError: unsupported operand type(s) for +: 'int' and 'str'.
To group values from a file into lists, use the counting pattern: a
new key starts as a list of one item, and a key seen before gets
append.
import os
quantities = {}
path = os.path.join("data", "sales.csv")
with open(path, "r") as f:
f.readline()
for line in f:
fields = line.strip().split(",")
product = fields[0]
quantity = int(fields[3])
if product in quantities:
quantities[product].append(quantity)
else:
quantities[product] = [quantity]
print(quantities){'P-01': [5, 4, 1], 'P-02': [2, 3]}
Sets
colors = {"red", "green", "blue", "red"}
print(len(colors))
print("red" in colors)
print(sorted(colors))3
True
['blue', 'green', 'red']
A set holds each item once, so the second
"red" is dropped. Its items have no order, so a set has no
indexes: colors[0] raises
TypeError: 'set' object is not subscriptable.
print(colors) can list the items in a different order on
each run, so print sorted(colors), which gives back a list
in order.
tags = set()
tags.add("python")
tags.add("data")
tags.add("python")
print(sorted(tags))
tags.remove("data")
print(sorted(tags))['data', 'python']
['python']
| Write | Does |
|---|---|
set() |
makes an empty set ({} makes an empty dictionary) |
set(items) |
makes a set of the items in a list, dropping duplicates |
s.add(x) |
adds x; does nothing if x is already
there |
s.remove(x) |
removes x; raises a KeyError if
x is missing |
x in s |
True if x is in s |
len(s) |
the number of items |
set is the quick way to count distinct items:
words = ["the", "cat", "the", "hat"]
unique = set(words)
print(len(words), len(unique))
print(sorted(unique))4 3
['cat', 'hat', 'the']
Sets are passed by reference, like lists and dictionaries. A function that changes its set changes the caller's set:
def add_tag(tags):
tags.add("new")
labels = {"sale"}
add_tag(labels)
print(sorted(labels))['new', 'sale']
Set Operations
a = {"red", "green", "blue"}
b = {"red", "yellow", "blue"}
print(sorted(a | b))
print(sorted(a & b))
print(sorted(a - b))
print(sorted(b - a))['blue', 'green', 'red', 'yellow']
['blue', 'red']
['green']
['yellow']
| Operator | Method | Name | Keeps the items |
|---|---|---|---|
a | b |
a.union(b) |
union | in either set |
a & b |
a.intersection(b) |
intersection | in both sets |
a - b |
a.difference(b) |
difference | in a but not in b |
Each operator makes a new set and leaves a and
b alone. Order matters only for -:
a - b and b - a differ.
first = set(input("Person 1: ").split(","))
second = set(input("Person 2: ").split(","))
print("Shared:", sorted(first & second))
print("All:", sorted(first | second))
print("Only person 1:", sorted(first - second))Person 1: chess,hiking,cooking
Person 2: hiking,gaming,chess
Shared: ['chess', 'hiking']
All: ['chess', 'cooking', 'gaming', 'hiking']
Only person 1: ['cooking']
Hashable Types
spots = {(1, 2): "x"}
print(spots[(1, 2)])
spots = {[1, 2]: "x"}x
TypeError: cannot use 'list' as a dict key (unhashable type: 'list')
Dictionary keys and set items must be hashable:
values that cannot change after they are made. Python 3.13 and older
print the error as TypeError: unhashable type: 'list'.
| Type | As a key or set item |
|---|---|
str, int, float |
works |
bool |
works |
tuple, like (1, 2) |
works |
list, like [1, 2] |
TypeError |
dict and set |
TypeError |
Values have no limit: a value can be a list, a dictionary, or a set.
Common Mistakes
| Code | Problem | Fix |
|---|---|---|
d["Sat"] for a key d does not have |
KeyError: 'Sat' |
check "Sat" in d first, catch the
KeyError, or d.get("Sat") |
menu[item] when the user typed
Coffee |
case and spaces make a different key | input(...).strip().lower() |
days["1"] when the keys are numbers |
"1" is not 1 |
days[int(text)] |
menu["item"] |
looks up the word item, not the variable |
menu[item] |
for x in d: to add up the values |
x is each key |
for x in d.values(): |
counts[key] += 1 for a new key |
KeyError: += needs an old value |
if/else, or
counts.get(key, 0) + 1 |
counts = [] to count |
TypeError: list indices must be integers or slices, not str |
counts = {} |
del d[key] inside for key in d: |
RuntimeError: dictionary changed size during iteration |
build a new dictionary |
students["name"] on a dictionary of records |
KeyError: the keys are ids |
students[sid]["name"] |
sum(scores) on a dictionary of lists |
adds the keys | sum(scores[name]) |
found = {} for an empty set |
{} is a dictionary, so found.add raises an
AttributeError |
found = set() |
colors[0] on a set |
TypeError: 'set' object is not subscriptable |
sorted(colors)[0] |
print(colors) on a set |
the order can change from run to run | print(sorted(colors)) |
| a list as a key or set item | TypeError: a list is not hashable |
use a tuple |
Glossary
- dictionary: A collection of key: value pairs that looks up each value by its key.
- difference:
a - b, the items in setathat are not in setb. - hashable: A value that cannot change after it is made, such as a string, a number, or a tuple. Only hashable values can be dictionary keys or set items.
- intersection:
a & b, the items in both sets. - key: The part of a pair used for lookups, such as
"Mon"in{"Mon": 50}. Each key appears once. - lookup table: A dictionary that turns one value into another, such as a day number into a day name.
- record: A dictionary whose keys name its fields,
such as
{"name": "Ana", "gpa": 3.6}. - set: A collection of distinct items in no
particular order, such as
{"red", "blue"}. - union:
a | b, the items in either set. - value: The part of a pair that a key looks up, such
as
50in{"Mon": 50}.