跟练这个项目,你就算真正入门 Python 了

学了那么多语法,不动手写一个完整的项目,等于没学。

这篇带你从零写一个命令行 Todo 应用。变量、数据结构、函数、类、文件读写、异常处理——全用上了。

项目长什么样?

>> 我的待办事项 <<
1. [x] [高] 学习Python
2. [ ] [中] 跑步30分钟
3. [ ] [低] 写周报

命令: add / complete / delete / list / save / quit

功能:

  • 添加、完成、删除待办
  • 设定优先级(高/中/低)
  • 数据持久化到 JSON 文件
  • 启动自动加载、退出自动保存

第一步:设计数据结构

{
    "title": "学习Python",
    "priority": "高",
    "completed": False,
    "created_at": "2026-06-23 16:30"
}

用字典存单个待办,用列表存全部。优先级用一个简单的枚举。

第二步:搭建骨架

import json
from datetime import datetime
from pathlib import Path

DATA_FILE = "todos.json"
PRIORITIES = {"高": "高", "中": "中", "低": "低"}


def load_todos():
    """从文件加载待办列表"""
    path = Path(DATA_FILE)
    if not path.exists():
        return []
    try:
        text = path.read_text(encoding="utf-8")
        return json.loads(text)
    except (json.JSONDecodeError, UnicodeDecodeError):
        print("数据文件损坏,已重置")
        return []


def save_todos(todos):
    """保存待办列表到文件"""
    with open(DATA_FILE, "w", encoding="utf-8") as f:
        json.dump(todos, f, ensure_ascii=False, indent=2)


def add_todo(todos):
    """添加待办"""
    title = input("待办内容:").strip()
    if not title:
        print("内容不能为空")
        return

    priority = input("优先级(高/中/低,默认中):").strip()
    if priority not in PRIORITIES:
        priority = "中"

    todo = {
        "title": title,
        "priority": priority,
        "completed": False,
        "created_at": datetime.now().strftime("%Y-%m-%d %H:%M"),
    }
    todos.append(todo)
    print(f"已添加:{title}")


def complete_todo(todos):
    """完成待办"""
    list_todos(todos)
    try:
        index = int(input("完成第几个?")) - 1
        if 0 <= index < len(todos):
            todos[index]["completed"] = True
            print(f"已完成:{todos[index]['title']}")
        else:
            print("序号无效")
    except ValueError:
        print("请输入数字")


def delete_todo(todos):
    """删除待办"""
    list_todos(todos)
    try:
        index = int(input("删除第几个?")) - 1
        if 0 <= index < len(todos):
            removed = todos.pop(index)
            print(f"已删除:{removed['title']}")
        else:
            print("序号无效")
    except ValueError:
        print("请输入数字")


def list_todos(todos):
    """显示待办列表"""
    if not todos:
        print("\n  还没有待办事项\n")
        return

    print(f"\n  {'我的待办事项':-^30}\n")
    for i, todo in enumerate(todos, 1):
        status = "x" if todo["completed"] else " "
        print(f"  {i}. [{status}] [{todo['priority']}] {todo['title']}")
    print()


def show_stats(todos):
    """显示统计"""
    total = len(todos)
    completed = sum(1 for t in todos if t["completed"])
    pending = total - completed
    print(f"\n  总计 {total} 项,已完成 {completed},未完成 {pending}\n")


def show_menu():
    """显示命令菜单"""
    print("命令:")
    print("  add     - 添加待办")
    print("  list    - 查看列表")
    print("  done    - 标记完成")
    print("  delete  - 删除待办")
    print("  stats   - 统计")
    print("  clear   - 清空已完成的")
    print("  save    - 保存到文件")
    print("  quit    - 保存并退出")
    print()


def clear_completed(todos):
    """清除已完成的待办"""
    before = len(todos)
    todos[:] = [t for t in todos if not t["completed"]]
    removed = before - len(todos)
    print(f"已清除 {removed} 项已完成的待办")


def main():
    todos = load_todos()
    show_stats(todos)

    while True:
        show_menu()
        cmd = input("> ").strip().lower()

        if cmd in ("add", "a"):
            add_todo(todos)
        elif cmd in ("list", "l"):
            list_todos(todos)
        elif cmd in ("done", "d"):
            complete_todo(todos)
        elif cmd in ("delete", "rm"):
            delete_todo(todos)
        elif cmd in ("stats", "s"):
            show_stats(todos)
        elif cmd in ("clear", "c"):
            clear_completed(todos)
        elif cmd in ("save", "w"):
            save_todos(todos)
            print("已保存")
        elif cmd in ("quit", "q", "exit"):
            save_todos(todos)
            print("再见!")
            break
        else:
            print(f"未知命令:{cmd},输入 help 或 ? 查看可用命令")
            show_menu()


if __name__ == "__main__":
    main()

第三步:一次加上所有好用的小功能

上面是基础版,下面是增强版,加上:

  • 按优先级排序
  • 搜索功能
  • 编辑已有待办
def sort_todos(todos):
    """按优先级排序(高→中→低),同优先级已完成排后面"""
    priority_order = {"高": 0, "中": 1, "低": 2}
    todos.sort(key=lambda t: (priority_order.get(t["priority"], 1), t["completed"]))


def search_todos(todos):
    """搜索待办"""
    keyword = input("搜索关键字:").strip()
    if not keyword:
        return
    results = [t for t in todos if keyword in t["title"]]
    if results:
        print(f"\n  找到 {len(results)} 条:")
        for i, todo in enumerate(results, 1):
            status = "x" if todo["completed"] else " "
            print(f"  {i}. [{status}] [{todo['priority']}] {todo['title']}")
    else:
        print("没找到匹配的待办")


def edit_todo(todos):
    """编辑待办"""
    list_todos(todos)
    try:
        index = int(input("编辑第几个?")) - 1
        if 0 <= index < len(todos):
            todo = todos[index]
            new_title = input(f"新内容(直接回车保留'{todo['title']}'):").strip()
            if new_title:
                todo["title"] = new_title
            new_priority = input(f"新优先级(直接回车保留'{todo['priority']}'):").strip()
            if new_priority in PRIORITIES:
                todo["priority"] = new_priority
            print("已更新")
        else:
            print("序号无效")
    except ValueError:
        print("请输入数字")

把这几个函数加到主菜单里,就是一个功能完整的 Todo 应用了。

用到的知识点回顾

知识点 用在哪儿
变量和数据类型 todos 列表,todo 字典
字符串操作 f-string 格式化输出
列表操作 appendpop、列表推导式
字典操作 todo["title"] 读写
条件判断 if cmd == "add" 命令路由
循环 while True 主循环
函数 add_todosave_todos
文件读写 json.loadjson.dump
异常处理 try/except ValueError
路径处理 Path 检查文件是否存在

一个 200 行的项目,前面学的东西全用上了。

还可以继续加的东西

如果你想把项目做得更完整:

  • 给待办加截止日期(datetime
  • 按多种方式排序(日期、优先级、完成状态)
  • 分类标签(工作/生活/学习)
  • argparse 支持命令行参数
  • 彩色输出(rich 库)
  • 导出为 Markdown

写在最后

这个 Todo 应用不是多高大上的东西,但它把一个 Python 新手应该会的所有知识点都串起来了。

建议你把代码从头敲一遍,不要复制粘贴。敲的过程中你会遇到缩进错误、变量名拼错、忘了冒号……每个错误都是一次学习。

全系列 15 篇文章到这里就结束了。从 print("Hello World") 到写出完整的命令行应用,希望这个系列帮你少走弯路。

有疑问的评论区聊。

更多推荐