A list is happy to hold 12000, 150 and 40. It just cannot tell you that 12000 was the rent and 150 was coffee. As soon as each number needs a name attached, a plain list starts to fight you. A dictionary is built for exactly this. It stores pairs, a key you look something up by and a value you get back, so a number stops being anonymous.
If Part 7 on lists, tuples and sets is behind you, you are ready. You need Python 3 and a text editor, nothing to install. Give this about fifteen minutes. Every block below is copy and run, and the output shown underneath is what you should see on your own screen. Terms get explained before they are used, because this series is for someone early in a coding career, not for someone who already knows the jargon.
What a dictionary holds
Think of the contacts on your phone. You do not scroll to position 47 to find a friend. You look them up by name and get their number back. A dictionary works the same way. The name is the key, the number is the value, and the lookup goes straight to the answer instead of walking through everything.
A dictionary looks up a value by its key.
Making one and reading it back
prices = {'rent': 12000, 'coffee': 150}
print(prices['rent'])
print(len(prices))12000 2
The curly braces make the dictionary. Each pair is written as key: value, and pairs are separated by commas. Square brackets do the lookup, so prices[‘rent’] means give me the value stored under ‘rent’. The len function counts how many pairs there are.
Run it with python prices.py on Windows, or python3 prices.py on macOS and Linux. One trap when you copy code off a web page: the straight quotes around ‘rent’ can arrive as curly quotes, and Python stops with a SyntaxError. If that happens, delete them and type the quotes yourself.
Reading a key that might not be there
The most common early stumble with dictionaries is asking for a key that does not exist. Watch what happens.
prices = {'rent': 12000, 'coffee': 150}
print(prices['metro'])Traceback (most recent call last):
File "missing.py", line 2, in <module>
print(prices['metro'])
~~~~~~^^^^^^^^^
KeyError: 'metro'Read the last line first. KeyError: ‘metro’ means the key metro is simply not in the dictionary. The last line of a traceback almost always names the real problem, so read it before anything above it. This is not a crash to fear. It is Python telling you plainly what it could not find.
When a key might be missing, use .get instead of square brackets. It returns None for a missing key, or a default you choose.
prices = {'rent': 12000, 'coffee': 150}
print(prices.get('metro'))
print(prices.get('metro', 0))None 0
| You write | Key exists | Key is missing |
|---|---|---|
| prices[‘metro’] | gives the value | raises KeyError, stops |
| prices.get(‘metro’) | gives the value | gives None, keeps going |
| prices.get(‘metro’, 0) | gives the value | gives 0, keeps going |
Square brackets stop on a missing key; .get keeps going.
Here is an opinion that goes against a lot of beginner tutorials. Many of them tell you to guard every lookup with if key in prices. That works, but it does two lookups where one would do, and it clutters simple code. Reach for .get(key, default) instead, and save in for the times you genuinely branch on whether something is present.
Adding, changing and removing entries
A dictionary can change after you make it. Assigning to a key adds it if it is new, or overwrites it if it already exists. The del keyword removes a pair.
prices = {'rent': 12000, 'coffee': 150}
prices['coffee'] = 180
prices['bus'] = 40
print('bus' in prices)
del prices['rent']
print(prices)True
{'coffee': 180, 'bus': 40}Assigning to ‘coffee’ replaced 150 with 180, because a key can appear only once. Assigning to ‘bus’ added a brand new pair. The in check answers a plain yes or no, and del took the rent pair out for good.
seen = {}
seen[['a', 'b']] = 1Traceback (most recent call last):
File "badkey.py", line 2, in <module>
seen[['a', 'b']] = 1
~~~~^^^^^^^^^^^^
TypeError: unhashable type: 'list'Walking through every pair
To visit each entry, loop over .items(). It hands you the key and the value together on every turn of the loop.
prices = {'rent': 12000, 'coffee': 150}
for name, cost in prices.items():
print(name, 'costs', cost)rent costs 12000 coffee costs 150
The two names name and cost pick up the key and value on each pass. If you want only the keys, loop over the dictionary itself or over .keys(). For only the values, use .values().
prices = {'rent': 12000, 'coffee': 150}
print(list(prices.keys()))
print(list(prices.values()))['rent', 'coffee'] [12000, 150]
| Task | Code | What you get |
|---|---|---|
| Make a dictionary | d = {‘a’: 1} | one pair, a maps to 1 |
| Read a key | d[‘a’] | the value, or KeyError |
| Read safely | d.get(‘a’, 0) | the value, or 0 |
| Add or change | d[‘b’] = 2 | adds b, or overwrites it |
| Remove a key | del d[‘a’] | the pair is gone |
| Is a key present | ‘a’ in d | True or False |
| Count pairs | len(d) | how many pairs |
| Loop over pairs | for k, v in d.items() | key and value each turn |
Storing many values under one key
A key can hold only one value at a time, but that value can be a list. That is how you keep several things grouped under the same label. The pattern is the same get with a default you saw earlier, except the default is an empty list.
rows = [
('food', 250),
('travel', 40),
('food', 120),
]
by_cat = {}
for category, amount in rows:
items = by_cat.get(category, [])
items.append(amount)
by_cat[category] = items
print(by_cat){'food': [250, 120], 'travel': [40]}For a new category, .get returns a fresh empty list, the amount is appended, and the list goes back under that key. For a category you have seen before, .get returns the list already there and the new amount joins it. You end with every amount grouped by its category, ready to total or count. This one dictionary of lists is enough to power a simple report, and it uses nothing beyond what you already know.
A worked example: totalling spend by category
Here is a real job a dictionary does well. You have a stack of expenses, each with a category and an amount, and you want the total spent per category. The trick is .get(category, 0): it starts a new category at zero, then adds to it every time that category shows up again.
rows = [
('food', 250),
('travel', 40),
('food', 120),
('fun', 220),
('travel', 60),
]
totals = {}
for category, amount in rows:
old = totals.get(category, 0)
totals[category] = old + amount
print(totals){'food': 370, 'travel': 100, 'fun': 220}The first time food appears, .get returns 0, so the total becomes 250. The second time, it returns 250 and the total climbs to 370. No category needs to be set up in advance, and nothing raises KeyError. That small pattern, get with a default then write back, is one you will reuse for the rest of your Python life.
Grouping amounts by category with .get and a running total.
print('My Expense Tracker')
expenses = []
while True:
category = input('Category or done: ')
if category == 'done':
break
amount = float(input('Amount: '))
note = input('Note: ')
expenses.append({
'category': category,
'amount': amount,
'note': note,
})
print('Saved', category)
print('Entries:', len(expenses))
for e in expenses:
print(e['category'], e['amount'], e['note'])My Expense Tracker Category or done: Food Amount: 250 Note: lunch Saved Food Category or done: Bus Amount: 40 Note: to office Saved Bus Category or done: done Entries: 2 Food 250.0 lunch Bus 40.0 to office
sentence = 'red blue red green blue red' # your code here
{'red': 3, 'blue': 2, 'green': 1}Show solution
sentence = 'red blue red green blue red'
counts = {}
for word in sentence.split():
counts[word] = counts.get(word, 0) + 1
print(counts)Dictionary questions that come up
Are dictionaries kept in order?
Yes. Since Python 3.7 a dictionary keeps its pairs in the order you inserted them, so a loop over it is predictable.
Can two keys be the same?
No. A later assignment to the same key overwrites the earlier value. If you need many values under one key, store a list as the value.
How do I loop over only the keys?
Loop over the dictionary directly, or over .keys(). For only the values, use .values(). For both together, use .items().
Can a value be another dictionary?
Yes. Nesting is common, and it is exactly how JSON from an API looks. You will meet that properly in Part 16.
When should I not use a dictionary?
When order and position are the point, or when you just have a plain sequence of items with no names, a list is the simpler fit. Reach for a dictionary when the data has labels.
You can now store data as key and value pairs, read it safely with .get, add, change and delete entries, loop over every pair, and total values by category. That covers most of what day to day Python code does with dictionaries.
Part 9 turns the blocks of code you keep rewriting into named functions you call in one line, and the expense tracker finally gets an add_expense you can reuse. If you practise one thing before then, rebuild the colour counter from memory. The full series map lives on the Complete Guide.


DrJha