Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
418: Building a Personal Expense Tracker
When you first sit down to build something like a personal expense tracker, the instinct is usually to get data on the screen as quickly as possible. You want to be able to add a coffee, a gym membership, and a grocery bill, and then see a total. Most people start by using a simple list of tuples or lists to store these records. It feels efficient because it's just a few lines of code.
The "List of Tuples" Trap
Imagine you start your tracker like this. You create a list called expenses, and every time you spend money, you append a tuple: ("Coffee", 4.50, "Food"). At first, this is great. You can loop through the list and sum up the second element of every tuple to get your total spend. But here is where I've seen this approach fall apart for almost every junior dev I've mentored: the moment you want to actually do something with the data, the code becomes unreadable.
# The naive way
expenses = [("Coffee", 4.50, "Food"), ("Gas", 40.00, "Transport")]
# Trying to calculate total for just 'Food'
food_total = 0
for e in expenses:
if e[2] == "Food":
food_total += e[1]
Look at those indices: e[2] and e[1]. To anyone else reading your code—including you, three weeks from now—those numbers are meaningless. Does index 1 represent the price or the date? Does index 2 represent the category or the vendor? If you decide to add a "Date" field to the start of your tuple, every single line of logic in your entire application breaks because all your index numbers have shifted. It's fragile, and it's a nightmare to maintain.
Structuring for Sanity with Dataclasses
The better way is to move away from generic sequences and toward structured objects. In Python, the dataclass is your best friend here. Instead of remembering that the price is at index 1, you give it a name. This transforms your data from a mystery pile of values into a set of clearly defined entities. I prefer this because it documents the code as you write it.
from dataclasses import dataclass
@dataclass
class Expense:
description: str
amount: float
category: str
# Now it's explicit
expenses = [Expense("Coffee", 4.50, "Food"), Expense("Gas", 40.00, "Transport")]
# This is infinitely more readable
food_total = sum(e.amount for e in expenses if e.category == "Food")
By using a dataclass, we've decoupled the structure of the data from the order of the data. If I add a date field to the Expense class, e.amount still works perfectly. We aren't guessing anymore; we're accessing properties.
Encapsulating Logic in a Manager Class
Now, while dataclasses fix the data representation, you still have the problem of "logic leakage." If you have sum() calls and if statements scattered all over your main script, your code becomes a tangled mess of data processing. I always recommend wrapping the collection in a manager class. Think of the ExpenseTracker class as the "brain" that knows how to handle the list of Expense objects.
The trade-off here is a bit more boilerplate upfront. You have to write a class and define methods like add_expense() or get_total_by_category(). But the payoff is huge: your main program loop becomes a series of high-level commands rather than a series of math operations. You stop worrying about how the total is calculated and just ask the tracker for the result. This is the difference between writing a script that works once and building a piece of software that you can actually expand without it collapsing under its own weight.
📋 Practical Task
Implement a Category-Based Budget Alert
You have a working Expense dataclass and an ExpenseTracker class that stores a list of expenses. Your task is to enhance the ExpenseTracker by adding a budget limit feature.
Modify the ExpenseTracker class to include:
- A dictionary called
budgetsthat maps category names (strings) to a maximum spending limit (floats). - A method called
set_budget(category, limit)to define or update a budget for a specific category. - An update to the
add_expense(expense)method: whenever a new expense is added, the tracker should check if the total spending for that expense's category has now exceeded its defined budget. If it has, the method should print a warning:"Warning: Budget exceeded for [Category]! Current spend: [Total]".
Ensure your logic handles cases where a category might not have a budget set yet without crashing the program.
There are no comments for now.