To-Do List in Python
Beginner · Useful tools · about 45 minutes to build · 109 lines
Add tasks, tick them off as you finish them and find them again next time, because the list is saved in a text file.
The program
Press Run and answer its questions in the box that appears under the code.
Open it in the full editor to save it, share it and keep building.
Sample run
What the program printed, and what was typed (in yellow).
To-do list: 0 task(s) loaded from todo.txt 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 1 New task: Finish maths homework Added: Finish maths homework 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 1 New task: Buy milk Added: Buy milk 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 1 New task: A task needs some words. Nothing added. 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 1 New task: Call grandma Added: Call grandma 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 3 1. [ ] Finish maths homework 2. [ ] Buy milk 3. [ ] Call grandma 0 of 3 done Task number to mark done (Enter to cancel): 2 Done: Buy milk 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 4 1. [ ] Finish maths homework 2. [x] Buy milk 3. [ ] Call grandma 1 of 3 done Task number to delete (Enter to cancel): seven Please type a number from 1 to 3. Task number to delete (Enter to cancel): 3 Deleted: Call grandma 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 2 1. [ ] Finish maths homework 2. [x] Buy milk 1 of 2 done 1) Add 2) List 3) Mark done 4) Delete 5) Quit Choose 1-5: 5 Your tasks are saved in todo.txt. Goodbye.
How it is built
1. How tasks are stored
Each task is one line in todo.txt, starting with [x] when it is done and [ ] when it is not. The file stays readable in any text editor.
FILE_NAME = "todo.txt" DONE_MARK = "[x] " OPEN_MARK = "[ ] " MENU = "1) Add 2) List 3) Mark done 4) Delete 5) Quit"
2. Loading, even on the first run
On the first run there is no file, so open() raises FileNotFoundError and the function returns an empty list. Each line becomes a dictionary with text and done.
def load_tasks(file_name): """Read the saved tasks, or return an empty list if there is no file yet.""" tasks = [] try: with open(file_name, encoding="utf-8") as file: for line in file: line = line.rstrip("\n") if line.startswith(DONE_MARK): tasks.append({"text": line[len(DONE_MARK):], "done": True}) elif line.startswith(OPEN_MARK): tasks.append({"text": line[len(OPEN_MARK):], "done": False}) elif line.strip(): # A line typed into the file by hand counts as not done tasks.append({"text": line.strip(), "done": False}) except FileNotFoundError: pass # First run: nothing has been saved yet return tasks
3. Saving after every change
Opening the file with "w" replaces what was there, so the file always matches the list in memory. main() calls this after each add, tick and delete, so closing the program loses nothing.
def save_tasks(file_name, tasks): """Write every task to the file, one per line.""" with open(file_name, "w", encoding="utf-8") as file: for task in tasks: mark = DONE_MARK if task["done"] else OPEN_MARK file.write(mark + task["text"] + "\n")
4. Choosing a task by number
People count from 1 and lists count from 0, so the function returns int(text) - 1. isdecimal() checks the answer is a number before int() runs, and Enter cancels.
def choose_task(tasks, action): """Ask for a task number and return its index, or None to cancel.""" if not tasks: print(" There are no tasks yet.") return None show_tasks(tasks) while True: text = input(f"Task number to {action} (Enter to cancel): ").strip() if text == "": return None if text.isdecimal() and 1 <= int(text) <= len(tasks): return int(text) - 1 print(f" Please type a number from 1 to {len(tasks)}.")
5. The menu
Each choice calls a small piece of code and saves the list when something changed. Marking a finished task again gets a message instead of a second save.
def main(): """Load the list, run the menu, and save after every change.""" tasks = load_tasks(FILE_NAME) print(f"To-do list: {len(tasks)} task(s) loaded from {FILE_NAME}") while True: print() print(MENU) choice = input("Choose 1-5: ").strip() if choice == "1": text = input("New task: ").strip() if text: tasks.append({"text": text, "done": False}) save_tasks(FILE_NAME, tasks) print(f" Added: {text}") else: print(" A task needs some words. Nothing added.") elif choice == "2": show_tasks(tasks) elif choice == "3": index = choose_task(tasks, "mark done") if index is not None and tasks[index]["done"]: print(" That task is already done.") elif index is not None: tasks[index]["done"] = True save_tasks(FILE_NAME, tasks) print(f" Done: {tasks[index]['text']}") elif choice == "4": index = choose_task(tasks, "delete") if index is not None: removed = tasks.pop(index) save_tasks(FILE_NAME, tasks) print(f" Deleted: {removed['text']}") elif choice == "5": print(f"Your tasks are saved in {FILE_NAME}. Goodbye.") break else: print(" Please choose a number from 1 to 5.")
Make it bigger
- Add a due date to each task and list overdue tasks first.
- Let the user change a task's text without deleting it.
- Add a Clear done option that removes every finished task at once.
- Store the tasks as JSON with the
jsonmodule so each task can carry more fields.