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
411: Building a Command-Line To-Do App
I've spent a lot of time building massive enterprise systems, but honestly, some of the best learning happens when you just try to build a tool you'll actually use. Today, we're tackling a CLI To-Do app. It sounds simple, but it's the perfect playground for handling state and file I/O.
The Memory Reset Problem
My first instinct is usually to just throw everything into a list. Let's try that. I'll start a script, create a list called tasks, and let the user add something to it.
tasks = []
while True:
item = input("Add a task (or type 'done'): ")
if item == 'done':
break
tasks.append(item)
print(f"Your list: {tasks}")
This works great while the script is running. I can add "Buy milk" and "Fix the leaky faucet," and the list grows. But here is the problem: the second I hit 'done' and the program exits, my list vanishes. If I restart the script, I'm back to square one. That's not a tool; that's a temporary notepad. I need the data to live somewhere outside the Python process.
Fighting the Memory Reset
The obvious fix is a text file. I'll try writing the list to a file every time I add an item. I'll use a simple .txt file where each task gets its own line.
# Let's try writing the list to a file
with open("todo.txt", "w") as f:
for task in tasks:
f.write(task + "\n")
Wait, I just realized a flaw in my logic. If I use "w" (write mode), Python wipes the file clean every time I open it. If I want to keep adding tasks without loading the whole list into memory first, I should use "a" for append. But then I have a different problem: how do I read those tasks back into the program when it starts up? I can't just read() the whole thing as one giant string.
I'll try readlines(). It gives me a list of strings, which is exactly what I want. One annoying detail, though—readlines() keeps the newline character \n at the end of every string. If I print my list now, every item is on a new line with an extra gap. I'll use a list comprehension with .strip() to clean that up.
with open("todo.txt", "r") as f:
tasks = [line.strip() for line in f.readlines()]
The Indexing Headache
Now that we can save and load, we need a way to remove tasks. I thought about just letting the user type the name of the task to delete it, but what if they have two tasks called "Clean room"? That's a recipe for deleting the wrong thing. Using an index (a number) is safer.
I tried this quickly: del tasks[int(input("Enter number to delete: "))]. It crashed immediately. Why? Because I typed "1" to delete the first item, but Python lists start at 0. I tried typing "0", and it worked, but no normal human wants to start counting their to-do list from zero. It feels unnatural.
The fix is a simple offset. I'll tell the user the list starts at 1, then subtract 1 from their input before passing it to the list. It's a small UX tweak, but it's the difference between a tool that feels broken and one that feels professional.
Tying the Loop Together
I've realized that having separate blocks of code for adding and deleting is messy. I need a main menu loop that keeps the app alive until I explicitly tell it to quit. I'll use a while True loop and a set of if/elif statements to route the user's choice.
def main():
# Load existing tasks first
try:
with open("todo.txt", "r") as f:
tasks = [line.strip() for line in f.readlines()]
except FileNotFoundError:
tasks = []
while True:
print("\n--- TO-DO APP ---")
print("1. View Tasks\n2. Add Task\n3. Remove Task\n4. Exit")
choice = input("Choose an option: ")
if choice == "1":
for i, task in enumerate(tasks, 1):
print(f"{i}. {task}")
elif choice == "2":
new_task = input("What needs doing? ")
tasks.append(new_task)
with open("todo.txt", "w") as f:
for t in tasks: f.write(t + "\n")
elif choice == "3":
idx = int(input("Task number to remove: ")) - 1
if 0 <= idx < len(tasks):
tasks.pop(idx)
with open("todo.txt", "w") as f:
for t in tasks: f.write(t + "\n")
else:
print("Invalid number!")
elif choice == "4":
break
main()
I noticed that I'm repeating the "save to file" logic in both the add and remove sections. That's a smell. In a real project, I'd move that into a save_tasks() function to keep things DRY (Don't Repeat Yourself). But for this exploration, the logic is clear: Load $\rightarrow$ Loop $\rightarrow$ Modify $\rightarrow$ Save.
📋 Practical Task
Build a Priority-Based Task Organizer
Modify the To-Do app we explored in the lesson to support priority levels. Instead of just storing a task name, each task should be stored as a string that includes a priority marker (e.g., "High: Buy Milk" or "Low: Water Plants").
- Modify the "Add Task" section to ask the user for a priority (High, Medium, or Low) before asking for the task description.
- Update the "View Tasks" section so that the app attempts to list all "High" priority tasks first, regardless of the order they were added.
- Ensure the tasks are still saved to and loaded from the
todo.txtfile so that the priorities persist across sessions.
There are no comments for now.