Lists and dictionaries
Three loose variables describe one expense. Track fifty and you'd need a hundred and fifty variables. Python's two workhorse containers — the dictionary and the list — fix that, and nearly every Python program you write will lean on both.
A dictionary groups related values
A dictionary maps keys to values. It keeps things that belong together in one object:
expense = {
"amount": 12.5,
"category": "food",
"note": "lunch with the team",
}
print(expense["category"])Square brackets read a value back by key. Unlike three separate variables, this whole expense can be passed to a function, stored in a list, or written to a file as a single unit — which is exactly what the rest of the series does with it.
Reading a key that doesn't exist raises KeyError. When a key is genuinely optional, .get() returns None instead of crashing:
print(expense.get("currency"))
print(expense.get("currency", "USD"))The second argument is the fallback, so the second line prints USD. Use .get() when a missing key is expected, and square brackets when a missing key means something is broken — the crash is useful information in that case.
A list holds many of them
A list is an ordered, growable sequence:
expenses = []
expenses.append({"amount": 12.5, "category": "food", "note": "lunch"})
expenses.append({"amount": 40.0, "category": "transport", "note": "train ticket"})
expenses.append({"amount": 8.75, "category": "food", "note": "coffee beans"})
print(len(expenses))append() adds to the end, and len() counts the items — 3 here. A list of dictionaries is the standard shape for "many records of the same kind" in Python, and it's what you'd get back from a CSV reader or a JSON API too.
Looping over the list
for walks through the items one at a time:
for expense in expenses:
print(f"{expense['category']:<12} ${expense['amount']:>8.2f} {expense['note']}")Two formatting details are doing real work here. :<12 pads the category to twelve characters and left-aligns it; :>8.2f right-aligns the amount in eight characters with two decimals. The result is columns that line up, which is the difference between a readable report and a wall of text.
Inside an f-string delimited by double quotes, use single quotes for dictionary keys: f"{expense['category']}". Reusing double quotes inside ends the string early and produces a syntax error.
Totals and filters
Summing a field is a one-liner. sum() takes any sequence of numbers, and a generator expression produces one from your list:
total = sum(expense["amount"] for expense in expenses)
print(f"Total: ${total:.2f}")That reads almost as English: the amount of each expense, added up. It prints Total: $61.25.
Filtering follows the same shape with a list comprehension, which builds a new list from the items that pass a condition:
food = [e for e in expenses if e["category"] == "food"]
food_total = sum(e["amount"] for e in food)
print(f"{len(food)} food expenses, ${food_total:.2f} total")The comprehension keeps only the matching dictionaries and leaves expenses untouched. Note == compares values while a single = assigns — using = in a condition is a syntax error in Python, which is a small mercy compared to languages where it silently succeeds.
Grouping by category
To total every category at once, build a dictionary as you go:
totals = {}
for expense in expenses:
category = expense["category"]
totals[category] = totals.get(category, 0) + expense["amount"]
print(totals)totals.get(category, 0) returns the running total if the category has been seen before, and 0 the first time — so a new key is created on first sight without a separate check. That prints {'food': 21.25, 'transport': 40.0}.
The data structures are in place. In the next part you'll stop repeating this logic inline and wrap it in functions.