跳到主要内容

10.2 实战项目二:待办清单

项目目标

做一个命令行版的 TODO 待办清单,支持:

  1. 查看所有待办
  2. 添加待办
  3. 完成(删除)待办
  4. 数据保存到文件,下次打开还在

用到的知识:列表、函数、文件读写、循环、异常处理。这是最接近"真实小工具"的项目。

程序结构设计

写代码前先想清楚结构(这一步比写代码更重要):

数据:todos = ["买牛奶", "写作业"] ← 一个列表存所有待办
文件:todo.txt ← 一行一条,永久保存

函数:
load_todos() 从文件读取待办,返回列表
save_todos(todos) 把列表写入文件
show_todos(todos) 显示所有待办
main() 主循环:显示菜单 → 接收选择 → 执行对应操作

完整代码

# todo.py — 命令行待办清单
import os

FILENAME = "todo.txt"


def load_todos():
"""从文件加载待办列表;文件不存在返回空列表"""
if not os.path.exists(FILENAME):
return []
with open(FILENAME, "r", encoding="utf-8") as f:
return [line.strip() for line in f if line.strip()]


def save_todos(todos):
"""把待办列表保存到文件"""
with open(FILENAME, "w", encoding="utf-8") as f:
for todo in todos:
f.write(todo + "\n")


def show_todos(todos):
"""显示所有待办"""
if not todos:
print("(空空如也,好好休息~)")
return
for i, todo in enumerate(todos, start=1):
print(f" {i}. {todo}")


def main():
todos = load_todos()

while True:
print("\n===== 待办清单 =====")
show_todos(todos)
print("--------------------")
print("1 添加 2 完成 3 退出")

choice = input("请选择:")

if choice == "1":
item = input("要添加的事项:").strip()
if item:
todos.append(item)
save_todos(todos)
print(f"已添加:{item}")
else:
print("不能添加空事项")

elif choice == "2":
if not todos:
print("没有待办可完成")
continue
try:
num = int(input("完成第几项:"))
done = todos.pop(num - 1) # 显示从 1 开始,索引从 0 开始
save_todos(todos)
print(f"✅ 已完成:{done}")
except (ValueError, IndexError):
print("无效的编号")

elif choice == "3":
print("再见!")
break

else:
print("请输入 1、2 或 3")


if __name__ == "__main__":
main()

值得细看的几个点

1. 列表推导式(一种简洁写法,第一次见可以先记住效果):

return [line.strip() for line in f if line.strip()]
# 等价于:
result = []
for line in f:
if line.strip(): # 跳过空行
result.append(line.strip())
return result

2. 编号换算:给用户看的编号从 1 开始,列表索引从 0 开始,所以 pop(num - 1)。这种"人类编号 vs 程序索引"的换算非常常见。

3. 一个 except 接住两种异常except (ValueError, IndexError): 同时处理"输入不是数字"和"编号超范围"。

4. 每次改动立即存盘append/pop 之后马上 save_todos,程序意外退出也不丢数据。

挑战任务

  1. 增加"清空全部"功能(菜单选项 4)。
  2. 完成的事项不直接删除,而是移到 done.txt 存档。
  3. 给每条待办加上添加时间(提示:datetime.now().strftime(...),存文件时用逗号分隔)。

你学到了什么

  • 先设计再编码:数据用什么结构存、拆成哪些函数
  • 加载/保存/显示各司其职,main 只负责调度——这就是模块化思维
  • 用户输入永远不可信,处处要校验

下一个项目:10.3 实战项目三:英文词频统计