Reference · module 11
Dictionaries and shapes
Everything this module explains, on one page.
Dictionaries
Store values under names instead of numbers.
- A list finds things by position and a dictionary finds them by name: user = {"name": "Anna", "age": 30}. Curly brackets, and each entry is a key, a colon, and a value.
- Reading looks like a list with the name in place of the number: user["name"] is "Anna". Which is why dictionaries win the moment your data has fields — nobody remembers that position 3 was the postcode.
- Asking for a key that is not there is a KeyError, and that is a feature: a typo in a field name stops the program at the typo rather than quietly handing back nothing.
The mistake you are about to make
user[0]
user["name"]
The brackets look exactly like a list, so a number feels natural. A dict looks things up by key, not position: user[0] is a KeyError unless 0 is a key.
Asking politely
Read a key that might not be there, and add one that is not.
- user.get("email") hands back None instead of raising when the key is missing, and user.get("email", "none") hands back whatever you name as the fallback. Use it when absence is normal.
- Writing is the same brackets as reading: user["email"] = "a@b.c" adds the key if it is missing and replaces the value if it is not. There is no separate add and update.
- And "email" in user asks whether the key is there at all, giving True or False. Note that it looks at keys, never at values — a mistake worth making once, here.
The mistake you are about to make
if "Anna" in user:
if "name" in user:
In on a dict looks like a search through everything and only looks at the keys. Better to make this mistake here than in running code.
Walking a dictionary
Go over every entry.
- for key in user: walks the keys, not the values — the same loop a list gives you, over the names. Getting the value is then user[key], which is ordinary lookup.
- for key, value in user.items(): gives both at once, and is what most loops over a dictionary want. Two names before the in, one pair out of the dictionary per pass.
- Building one is the same shape as building a list: start with an empty {} before the loop and assign a key inside it. The empty dictionary and the empty set look alike; {} is the dictionary.
The mistake you are about to make
for value in user:
for key, value in user.items():
Looping over a dict looks like looping over a list and seems to promise the values. It gives the keys. Both together come from .items().
Tuples
Use the list that cannot be changed, and unpack it.
- A tuple is a list that cannot be changed: point = (3, 4). Round brackets instead of square, indexing works the same, and point[0] = 9 is a TypeError.
- Which sounds like a restriction and is a guarantee. A tuple is for things whose shape is fixed — a coordinate, a date, a row — and passing one around means nothing downstream can quietly rewrite it.
- Unpacking gives the parts names in one line: x, y = point. It is the same move as for key, value in items(), and it works on lists too — the tuple is just where it is used most.
The mistake you are about to make
point[0] = 9
point = (9, point[1])
A tuple looks like a list in different brackets, so assignment seems fine. It never changes: the only move is to build a new one.
Data with a shape
Read a list of dictionaries — the shape real data arrives in.
- Real data is usually a list of dictionaries: users = [{"name": "Anna"}, {"name": "Ben"}]. Every row has the same fields, and the list holds the rows. Every API you meet hands back this shape.
- Reaching inside is one step at a time, left to right: users[0] is the first dictionary, and users[0]["name"] is the name in it. Read the brackets in order and nothing is mysterious.
- And the loop is the one you already have: for user in users: gives a whole dictionary each pass, so user["name"] inside is an ordinary lookup. Nothing new — the shapes just stack.
The mistake you are about to make
users["name"]
users[0]["name"]
A list of dicts looks like one big dict, so the field gets asked for directly. The steps go left to right: the record first, then its field.