【Copy-Paste to Run】Let's Build a Simple To-Do List App in Python! (Text-Based)
To run Python from the command prompt or PowerShell on your PC, you need to download and install Python.
If you haven’t installed it yet, please refer to the article Setting Up Python and Development Environment to install Python.
Hello! I had zero programming knowledge just a few months ago, and now I'm sharing what I learned while building websites with AI.
This time, we'll create a "To-Do List App" in Python, which I highly recommend as a first step in learning to program. The goal of this article is simple: for you to experience the thrill of "My own program is running!"
Let's leave the difficult theory for later! First, let's play with code that you can copy and paste to get a feel for, "Oh, so that's how it works!" I'll explain the technical terms in a super simple way based on my own stumbling blocks, so don't worry.
Let's Run the Final Product First!
Seeing is believing! Let's run the finished version of the To-Do list app first. Copy all the code below, create a file named todo.py, and paste it in.
# -*- coding: utf-8 -*-
TASKS_FILE = "tasks.txt"
def load_tasks():
"""Function to load tasks from a file"""
try:
with open(TASKS_FILE, "r", encoding="utf-8") as f:
# Use strip() to remove the newline character at the end of each line
tasks = [line.strip() for line in f.readlines()]
except FileNotFoundError:
# If the file doesn't exist, return an empty list
tasks = []
return tasks
def save_tasks(tasks):
"""Function to save tasks to a file"""
with open(TASKS_FILE, "w", encoding="utf-8") as f:
# Add a newline after each task before writing
for task in tasks:
f.write(task + "\n")
def list_tasks(tasks):
"""Function to list all tasks"""
if not tasks:
print("✅ No tasks yet.")
return
print("--- To-Do List ---")
# Use enumerate to get both the index (number) and the task
for i, task in enumerate(tasks):
print(f"{i + 1}: {task}")
print("------------------")
def add_task(tasks):
"""Function to add a task"""
task = input("Enter the task to add: ")
if task:
tasks.append(task)
save_tasks(tasks)
print(f"✨ Task '{task}' added.")
else:
print("⚠️ No task entered.")
def delete_task(tasks):
"""Function to delete a task"""
list_tasks(tasks)
if not tasks:
return
try:
# Let the user choose a task by its number
num_str = input("Enter the number of the task to delete: ")
num = int(num_str)
# Adjust because the user counts from 1, but the list index starts at 0
if 1 <= num <= len(tasks):
removed_task = tasks.pop(num - 1)
save_tasks(tasks)
print(f"🗑️ Task '{removed_task}' deleted.")
else:
print("⚠️ No task with that number exists.")
except ValueError:
# Error handling for non-numeric input
print("⚠️ Please enter a valid number.")
def main():
"""Main function to run the process"""
tasks = load_tasks()
while True:
print("\nWhat would you like to do?")
command = input("1:List, 2:Add, 3:Delete, 9:Exit > ")
if command == "1":
list_tasks(tasks)
elif command == "2":
add_task(tasks)
elif command == "3":
delete_task(tasks)
elif command == "9":
print("👋 Exiting the app. Great work!")
break
else:
print("⚠️ Please enter 1, 2, 3, or 9.")
if __name__ == "__main__":
main()
Once saved, open your terminal (or Command Prompt/PowerShell on Windows), navigate to the directory where you saved the file, and run the following command:
python todo.py
How did it go? Did it ask "What would you like to do?" Try entering "2" to add a task, "1" to see the list, and "3" to delete a task. You can exit the app by entering "9". Amazing, it's already a proper app!
From here, let's unravel what this magic spell (the code) is actually doing.
Step 1: A Super Simple Version with Only "Add" and "List"
Trying to understand everything at once can be overwhelming. Let's start with a much simpler version of the code, focusing only on the heart of the app: adding and listing tasks.
# Prepare an empty list (like an array) to store tasks
tasks = []
# Loop indefinitely
while True:
print("\nWhat would you like to do?")
command = input("1:List, 2:Add, 9:Exit > ")
if command == "1":
print("--- To-Do List ---")
for task in tasks:
print(task)
print("------------------")
elif command == "2":
task = input("Enter the task to add: ")
tasks.append(task)
print(f"✨ Task '{task}' added.")
elif command == "9":
print("👋 Exiting the app.")
break # Exit the loop
else:
print("⚠️ Please enter 1, 2, or 9.")
This short piece of code is packed with programming fundamentals.
tasks = []: This is the "list" for storing tasks. Imagine it as preparing an empty box.while True:: This is an "infinite loop." It will keep waiting for the user's command until "9" is entered. This was the magic spell AI taught me when I first asked, "How do I make the program wait?"input(...): This accepts characters typed by the user on the keyboard.if / elif / else: This separates the actions based on the value of the inputcommand. It's a conditional branch, like "If it's 1, list tasks; if it's 2, add a task; otherwise..."tasks.append(task): This adds the task received frominputto the end of thetaskslist.for task in tasks:: This takes each item from thetaskslist, one by one from the beginning, puts it into a variable calledtask, and displays it on the screen withprint(task).break: This is the escape switch to end the infinite loop. If you forget this, you have no choice but to force-quit the program (I've done that many times...!).
Step 2: Adding a "Delete" Function and Error Handling
Next, let's make it possible to delete added tasks. Just deleting isn't very user-friendly, so we'll let the user choose which task to delete by number. We're leveling up a bit here!
# ... (Replace the `if command == "1":` part from Step 1's code with the following) ...
if command == "1":
print("--- To-Do List ---")
# Using enumerate gets you the index and the item at the same time
for i, task in enumerate(tasks):
# i starts from 0, so add 1 for display
print(f"{i + 1}: {task}")
print("------------------")
# ... (Add the following after `elif command == "2":` from Step 1's code) ...
elif command == "3": # Add delete functionality
# First, display the current list to make it easier for the user to choose
for i, task in enumerate(tasks):
print(f"{i + 1}: {task}")
try:
num_str = input("Enter the number of the task to delete: ")
num = int(num_str) # Convert the input string to a number
# pop() removes the element at the specified index from the list
removed_task = tasks.pop(num - 1)
print(f"🗑️ Task '{removed_task}' deleted.")
except ValueError:
print("⚠️ Please enter a valid number.")
except IndexError:
print("⚠️ No task with that number exists.")
A few new points have come up.
enumerate(tasks): This is incredibly useful! When you loop through a list, it automatically assigns a number "0, 1, 2..." to each item. This allows us to display tasks with numbers, likef"{i + 1}: {task}".tasks.pop(num - 1): This is the key to deletion.pop()removes an element from a list at a specified position. It'snum - 1because while the user enters "1," the programming world (list indices) starts counting from "0," so we need to shift it by one. This "zero-based indexing" is a point of confusion for every programming beginner, so it's good to remember!try / except: This is the error-handling mechanism. It means, "Go ahead and try to run the code insidetry:. If an error occurs, catch it withexcept:and run this code instead of crashing."ValueError:int()converts a string to a number, but this error occurs if it's given a non-numeric string like "a".IndexError: This error occurs if you try topop()a non-existent index from the list (e.g., specifying "5" when there are only 3 tasks).
It's a golden rule that "users won't operate the way the developer expects." By including error handling like this, you can prevent your app from crashing unexpectedly and create a more user-friendly and robust program.
Step 3: Saving Data to a File So It Doesn't Disappear
Our current app has a major weakness. Once you close the program, all the added tasks disappear. That makes it useless as a to-do list, right? So, let's add a feature to save tasks to a file and load them the next time it starts.
# Decide on a filename
TASKS_FILE = "tasks.txt"
# --- Function Definitions ---
def load_tasks():
try:
# "r" is for read mode
with open(TASKS_FILE, "r", encoding="utf-8") as f:
# readlines() reads all lines into a list, and strip() removes newline characters
tasks = [line.strip() for line in f.readlines()]
except FileNotFoundError:
# This will error out the first time or if the file is missing, so return an empty list
tasks = []
return tasks
def save_tasks(tasks):
# "w" is for write mode. If the file doesn't exist, it will be created.
with open(TASKS_FILE, "w", encoding="utf-8") as f:
for task in tasks:
# When writing to a file, add a newline character (\n) to the end of each task
f.write(task + "\n")
# --- Main Process ---
# When the app starts, first load the tasks from the file
tasks = load_tasks()
# Every time a task is added or deleted, change it to call save_tasks(tasks)
# (Refer to the complete code at the beginning)
The key point here is "file I/O" (Input/Output).
with open(...) as f:: This is the standard phrase for handling files in Python. Writing it this way ensures the file is automatically closed after reading or writing is finished, which is safer. When I asked AI, "How do I handle files safely in Python?" this was the first thing it recommended.- Modes ("r", "w"): The second argument of
open()specifies how to open the file."r"is for read-only, and"w"is for write-only. Be careful, as"w"mode will erase the entire content of an existing file before writing. encoding="utf-8": Just think of this as a magic spell. It's necessary to handle non-English characters correctly and can cause garbled text if omitted.- Reading (load):
f.readlines()reads the file's content into a list of lines. However, each line includes a newline character (\n) at the end, so we useline.strip()to remove it and clean up the data. - Writing (save):
f.write()writes a string to the file. This time, we do the opposite: to save each task on a separate line, we add a newline character\nto the end of the task string. If you forget this, all tasks will be joined on a single line and won't load correctly (I spent about 15 minutes stumped on this...).
Finally, as shown in the complete code at the beginning, grouping blocks of processing into "functions" using def (this is called refactoring) makes the code much cleaner and easier to understand. When your code starts getting long, making a habit of organizing it into boxes (functions) for each feature will make your future self's life much easier.
Conclusion: If It Runs, You Win! And On to the Next Step
Great job! You now have the power to create your own To-Do list app in Python. The most important thing is not to understand everything perfectly, but to "just try running it, even if it's by copy-pasting." Having something that works and then tweaking it little by little, experimenting with "what happens if I change this?", is the fastest path to improvement, as I've experienced firsthand.
Errors are not scary; they are hints to make your program better. Even if you can't read an error message, you can just paste it into an AI and ask, "What does this error mean, and how can I fix it?" and it will usually give you a solution.
Feel free to add features like "edit task" or "check off completed tasks" to the app you built today, and grow it into your own ultimate To-Do list!
Next Steps
Now that you can make a basic app, why not try tackling calculations next? Here's how to build a simple calculator app that can perform the four basic arithmetic operations.
Let's Build a Calculator App in Python (Arithmetic Operations)