Syllabus Lesson 64 of 239 · Pythonic Code, Testing & Capstone
Pythonic Code, Testing & Capstone

Capstone: Expense Tracker (data layer)

For the next two lessons you build a real thing: an Expense Tracker, the engine behind a tiny command-line money app. No fill-in-the-blank. You get a plain-English spec and a near-blank file, and you architect the rest. Part 1 is the data layer -> four pure functions over a ledger. Part 2 wraps them in a runnable app you can drive with commands.

A ledger is just a list of dicts. One expense looks like this:

{"description": "Coffee", "amount": 3.5, "category": "food"}

Pure functions take data in and hand data back. No printing, no globals, no surprises -> which is exactly why they are a joy to test. Build these four:

add_expense(ledger, desc, amount, category)

Returns a new ledger -> the old one plus one fresh expense dict (keys "description", "amount", "category"). Do not mutate the list you were handed; return a new one with ledger + [new_dict]. That immutability keeps callers safe.

ledger = []
ledger = add_expense(ledger, "Coffee", 3.5, "food")
ledger = add_expense(ledger, "Bus", 2.0, "transport")

total(ledger)

The grand total of every amount. An empty ledger totals to 0. A one-line sum(...) over a generator expression does it.

total_by_category(ledger)

A dict mapping each category to the sum of its amounts -> the classic accumulate-into-a-dict loop. Start with {} and use totals.get(category, 0) to default a new category to zero before adding to it.

total_by_category(ledger)
# {"food": 3.5, "transport": 2.0}

top_category(ledger)

The single category with the highest total. Build on total_by_category, then reach for max(d, key=d.get). For an empty ledger there is no top category -> return the empty string "".

Press Run to grade. The hidden tests build a small ledger and check every value exactly. Get all four green and you have a tested data layer -> Part 2 turns it into an app you can actually talk to.

Your turn

Write four pure functions over a ledger (a list of expense dicts with keys description, amount, category). add_expense(ledger, desc, amount, category) returns a NEW ledger with one appended dict and must not mutate the input. total(ledger) returns the summed amount (0 when empty). total_by_category(ledger) returns a dict of category -> summed amount ({} when empty). top_category(ledger) returns the category with the largest total, or "" when empty.

Spotted a problem in this lesson? Report it

Code · runs in your browser
Output