Dictionaries
A dictionary (dict) maps keys to values: a word to its definition, a username to a profile, a product code to its price. Lookups by key are extremely fast, and dictionaries are the natural way to represent structured records — JSON data, for example, becomes nested dictionaries in Python.
Creating dictionaries
person = {"name": "Ana", "age": 31, "city": "Lisbon"}
empty = {}
from_pairs = dict([("a", 1), ("b", 2)])
from_kwargs = dict(width=800, height=600)
print(person)
print(from_pairs, from_kwargs)
print(len(person))
{'name': 'Ana', 'age': 31, 'city': 'Lisbon'}
{'a': 1, 'b': 2} {'width': 800, 'height': 600}
3Keys must be hashable (strings, numbers, tuples); values can be anything. Dictionaries remember the order in which keys were inserted.
Reading values
person = {"name": "Ana", "age": 31}
print(person["name"])
print(person.get("email"))
print(person.get("email", "not provided"))
print("age" in person, "email" in person)
Ana None not provided True False
Square brackets raise an error for a missing key; get returns None or a default you supply. in checks whether a key exists.
person = {"name": "Ana"}
print(person["email"])
Traceback (most recent call last):
File "main.py", line 2, in <module>
print(person["email"])
~~~~~~^^^^^^^^^
KeyError: 'email'Adding, changing and removing
stock = {"apples": 10, "pears": 4}
stock["plums"] = 7
stock["apples"] -= 3
print(stock)
removed = stock.pop("pears")
print(removed, stock)
del stock["plums"]
print(stock)
print(stock.pop("kiwis", 0))
{'apples': 7, 'pears': 4, 'plums': 7}
4 {'apples': 7, 'plums': 7}
{'apples': 7}
0Assigning to a key adds it or replaces its value. pop(key, default) removes and returns a value without erroring if the key is missing.
Looping
prices = {"tea": 3.0, "cake": 4.5, "juice": 5.0}
for item in prices:
print(item)
print(list(prices.values()))
for item, price in prices.items():
print(f"{item}: ${price:.2f}")
tea cake juice [3.0, 4.5, 5.0] tea: $3.00 cake: $4.50 juice: $5.00
Looping over a dictionary gives its keys; .values() gives values; .items() gives (key, value) pairs, which you'll use most.
Counting with a dictionary
text = "the cat and the hat and the bat" counts = {} for word in text.split(): counts[word] = counts.get(word, 0) + 1 print(counts) print(max(counts, key=counts.get))
{'the': 3, 'cat': 1, 'and': 2, 'hat': 1, 'bat': 1}
thecounts.get(word, 0) + 1 is the classic counting idiom. The collections.Counter class (lesson 27) does it in one line.
Nested dictionaries
users = {
"ana": {"email": "ana@x.com", "roles": ["admin", "editor"]},
"ben": {"email": "ben@x.com", "roles": []},
}
print(users["ana"]["email"])
print(users["ana"]["roles"][0])
users["ben"]["roles"].append("viewer")
for name, info in users.items():
print(f"{name}: {', '.join(info['roles']) or 'no roles'}")
ana@x.com admin ana: admin, editor ben: viewer
Merging dictionaries
defaults = {"theme": "light", "font_size": 14, "autosave": True}
user_prefs = {"theme": "dark"}
settings = defaults | user_prefs
print(settings)
defaults |= {"font_size": 16}
print(defaults["font_size"])
combined = {**defaults, **user_prefs, "lang": "en"}
print(combined)
{'theme': 'dark', 'font_size': 14, 'autosave': True}
16
{'theme': 'dark', 'font_size': 16, 'autosave': True, 'lang': 'en'}With | and ** unpacking, later values win. |= updates a dictionary in place, like .update().
Keys must be hashable
locations = {(38.7, -9.1): "Lisbon", (41.1, -8.6): "Porto"}
print(locations[(38.7, -9.1)])
print({1: "int", 1.0: "float", True: "bool"})
Lisbon
{1: 'bool'}Tuples make good composite keys. The second line shows a curiosity: 1, 1.0 and True are equal and hash the same, so they count as one key.
Dictionary views
keys(), values() and items() return live views that reflect later changes, and key views support set operations:
a = {"x": 1, "y": 2, "z": 3}
b = {"y": 20, "w": 40}
keys = a.keys()
a["new"] = 0
print(list(keys))
print(sorted(a.keys() & b.keys()), sorted(a.keys() - b.keys()))
['x', 'y', 'z', 'new'] ['y'] ['new', 'x', 'z']