Imagine you have a thousand prices and you need the cost of 'apple'. With a list, you scan every item until you find it — slow, and the code is a loop inside a loop. A dictionary solves this in one step: you look up the key, and Python jumps straight to the value.
A dictionary maps keys to values. You write it with curly braces, a colon between each key and value, and commas between pairs:
prices = {"apple": 2, "banana": 3, "cherry": 5}
Keys must be unique and hashable — strings and numbers work fine. Values can be anything: numbers, strings, lists, even other dictionaries. Think of a dict as a set of labelled boxes: the label is the key, and the contents are the value.
Because lookup is fast and does not slow down as the dict grows, dictionaries are the standard tool for counting, grouping, caching, and configuration. Whenever your code asks "what is the value for this name?", a dict is almost always the right answer.
flowchart LR K1["key 'apple'"] --> V1["value 2"] K2["key 'banana'"] --> V2["value 3"] K3["key 'cherry'"] --> V3["value 5"] style K1 fill:#3776ab,color:#fff style K2 fill:#3776ab,color:#fff style K3 fill:#3776ab,color:#fff
Reading and writing
Use square brackets to read a value or assign one:
print(prices["apple"]) # 2
prices["apple"] = 1 # update an existing key
prices["date"] = 4 # add a brand-new key
Assigning to an existing key overwrites the old value — there is no warning, because uniqueness is the whole point of a dictionary. Assigning to a new key creates it on the spot; dictionaries grow dynamically.
But here is the trap every beginner hits: if the key does not exist, prices["kumquat"] crashes with a KeyError. That is fine when you know the key is there, but when it might be missing — user input, configuration files, or data from another system — you need a safer approach.
One rule of thumb: d[key] is for "I am certain this key exists". When you are not certain, reach for .get() instead.
prices = {"apple": 2, "banana": 3, "cherry": 5}
print(prices["apple"])
prices["apple"] = 1
prices["date"] = 4
print(prices)
The safe way: .get() and membership testing
.get(key, default) returns the value if the key exists, or the default if it does not. No crash, no try/except needed:
print(prices.get("apple", 0)) # 1 — apple is there
print(prices.get("kumquat", 0)) # 0 — kumquat is missing
You can also test whether a key exists with the in keyword. "apple" in prices is True; "kumquat" in prices is False. This is fast — dictionaries are built for quick lookups — and it reads like plain English.
These two tools together cover almost every safe-access pattern. Use if key in d when you need a branch, and use .get() when you need a fallback value in one expression. For example, if fruit in prices: print(prices[fruit]) is safe because you checked first; prices.get(fruit, 0) is safe because it supplies its own default.
prices = {"apple": 2, "banana": 3}
print(prices.get("apple", 0))
print(prices.get("dragonfruit", 0))
print("apple" in prices)
print("dragonfruit" in prices)
# Safe branch
for fruit in ["apple", "dragonfruit"]:
if fruit in prices:
print(fruit, "is", prices[fruit])
else:
print(fruit, "not available")
flowchart TD
S["prices.get('apple', 0)"] --> C{"key exists?"}
C -->|yes| V["return value 2"]
C -->|no| D["return default 0"]
style S fill:#3776ab,color:#fff
style V fill:#16a34a,color:#fff
style D fill:#b45309,color:#fff
Iterating over dictionaries
Three methods give you every entry, each from a different angle:
.keys()— every key..values()— every value..items()— every(key, value)pair.
Most of the time you want .items(), because it gives you both pieces at once and you can unpack them into two variables:
for fruit, price in prices.items():
print(fruit, "costs", price)
Looping order matches insertion order in Python 3.7+, which is the version you are using. That predictability makes dictionaries reliable for ordered data too.
If you only need the values — for example, to sum every price — loop over .values() directly. If you only need the keys, use .keys(). Each method is clearer than looping over the dict and indexing back in. You can also wrap any of them in list() if you need a real list instead of a view.
prices = {"apple": 2, "banana": 3, "cherry": 5}
print("Keys:", list(prices.keys()))
print("Values:", list(prices.values()))
print("Total:", sum(prices.values()))
for fruit, price in prices.items():
print(fruit, "costs", price)
flowchart LR
D["{'apple': 2, 'banana': 3}"] --> I[".items()"]
I --> P1["fruit='apple'\nprice=2"]
I --> P2["fruit='banana'\nprice=3"]
style D fill:#3776ab,color:#fff
style I fill:#1e293b,color:#fff
Write word_lengths(words) that returns a dictionary mapping each word to its length. (Example: word_lengths(["cat", "hi"]) → {"cat": 3, "hi": 2}.)
def word_lengths(words):
result = {}
# store len(w) for each word w
return result
Loop with
for w in words:.Assign each entry with
result[w] = len(w).
def word_lengths(words):
result = {}
for w in words:
result[w] = len(w)
return result
What does this print? items() gives each key and value as a pair.
prices = {"apple": 2, "banana": 3}
for fruit, price in prices.items():
print(fruit, price)
prices.items()yields (key, value) pairs in order.print(fruit, price)puts a space between them.
Write merge_counts(a, b) that takes two dictionaries mapping strings to integers and returns a new dictionary with the same keys, where each value is the sum of the values from a and b. If a key is missing from one dict, treat it as 0. Use .get() to stay safe.
def merge_counts(a, b):
# return a new dict with summed values
pass
Loop over all unique keys from both dicts:
set(a) | set(b).Use
.get(key, 0)so missing keys contribute 0.
def merge_counts(a, b):
result = {}
for key in set(a) | set(b):
result[key] = a.get(key, 0) + b.get(key, 0)
return result
This function is supposed to return the price of a fruit, or 0 if it's not in the menu. It crashes on missing keys. Fix it using .get().
def fruit_price(menu, fruit):
return menu[fruit]
Replace
menu[fruit]withmenu.get(fruit, 0).The default of 0 is returned when the key is missing.
def fruit_price(menu, fruit):
return menu.get(fruit, 0)
Write invert_dict(d) that takes a dictionary and returns a new dictionary with keys and values swapped. If a value appears more than once, the last key wins. (Example: invert_dict({"a": 1, "b": 2}) → {1: "a", 2: "b"}.)
def invert_dict(d):
# swap keys and values
pass
Loop over
d.items()to get both key and value.Assign
result[v] = kto swap them.
def invert_dict(d):
result = {}
for k, v in d.items():
result[v] = k
return result
Recap
- A dictionary maps keys to values in one step.
d[key]reads or writes, but crashes withKeyErrorif the key is missing..get(key, default)is the safe alternative that never crashes.- Use
key in dto test membership without fetching the value. .items()is the most common way to loop, unpacking intokey, valuepairs.- Dictionary lookup is fast and stays fast as the data grows — when you need to find things by name, reach for a dict before a list.
Next you'll learn to handle the errors that happen when code goes wrong, including what to do when a KeyError really does escape.