Tutorial12 min2026-05-20

Build Your First Python Project: Expense Tracker (Complete Beginner Tutorial)

Build a real Python expense tracker from scratch — CSV reading, category analysis, budget checking. No templates, no starter code.

Build Your First Python Project: Expense Tracker (Complete Beginner Tutorial)

The fastest way to learn Python is not to watch more tutorials. It is to build something. Today you are building a real Python project from scratch — simple enough to finish in one sitting, complex enough to teach you skills you will use in every project after this.

Setup

Make sure you have Python 3.12 or later installed. Create a folder called expense-tracker. Create a file called expenses.csv with sample data:

snippet.text
1date,description,amount,category
22026-01-01,Coffee,4.50,Food
32026-01-02,Netflix,15.99,Entertainment
42026-01-03,Groceries,67.30,Food
52026-01-04,Gym,29.99,Health
62026-01-05,Amazon,43.20,Shopping
72026-01-06,Electricity,85.00,Bills
82026-01-07,Restaurant,38.50,Food

Step 1: Read the Data

snippet.py
1import csv
2from collections import defaultdict
3
4def read_expenses(filename):
5    expenses = []
6    with open(filename, 'r') as file:
7        reader = csv.DictReader(file)
8        for row in reader:
9            expenses.append({
10                'date': row['date'],
11                'description': row['description'],
12                'amount': float(row['amount']),
13                'category': row['category']
14            })
15    return expenses
16
17expenses = read_expenses('expenses.csv')
18print(f"Loaded {len(expenses)} expenses")

csv.DictReader reads the CSV and treats the first row as column headers. float(row["amount"]) converts text "4.50" into the number 4.50 so we can do maths with it. We build a list of dictionaries — one dictionary per expense row.

Step 2: Analyse by Category

snippet.py
1def analyse_by_category(expenses):
2    categories = defaultdict(float)
3    for expense in expenses:
4        categories[expense['category']] += expense['amount']
5    return dict(categories)
6
7def display_summary(expenses):
8    total = sum(e['amount'] for e in expenses)
9    categories = analyse_by_category(expenses)
10
11    print("\n" + "=" * 40)
12    print("EXPENSE SUMMARY")
13    print("=" * 40)
14    print(f"Total spent: ${total:.2f}")
15    print("\nBy category:")
16
17    sorted_categories = sorted(
18        categories.items(),
19        key=lambda x: x[1],
20        reverse=True
21    )
22    for category, amount in sorted_categories:
23        percentage = (amount / total) * 100
24        print(f"  {category}: ${amount:.2f} ({percentage:.1f}%)")

defaultdict(float) creates a dictionary that automatically starts new keys at 0.0. sorted() with key=lambda x: x[1] sorts by the second element of each tuple. :.2f in f-strings formats numbers to exactly 2 decimal places.

Step 3: Budget Checker

snippet.py
1BUDGETS = {
2    'Food': 150.00,
3    'Entertainment': 50.00,
4    'Health': 100.00,
5    'Shopping': 100.00,
6    'Bills': 200.00
7}
8
9def check_budgets(expenses):
10    categories = analyse_by_category(expenses)
11    print("\nBudget status:")
12    for category, budget in BUDGETS.items():
13        spent = categories.get(category, 0)
14        remaining = budget - spent
15        status = "OK" if remaining >= 0 else "OVER BUDGET"
16        print(f"  {category}: ${spent:.2f} / ${budget:.2f}{status}")
17        if remaining < 0:
18            print(f"    ⚠  Over by ${abs(remaining):.2f}")

What You Actually Learned

File I/O, CSV parsing, dictionaries, functions, list sorting, lambda functions, f-strings, default arguments, defaultdict, and constants. Every one of these concepts appears constantly in real Python projects.

Do not move to the next tutorial yet. Instead, modify this project. Add a new expense category, calculate the average daily spend, filter expenses by date range, or add colour to the terminal output.

Ready to Level Up?

Stop guessing what to learn next. Get the structured path that separates senior engineers from everyone else.

Grab Your Free Roadmap