【Python】基础语法入门(二十)——项目实战:从零构建命令行 To-Do List 应用

🛠️ 说明 :经过前十九篇的学习,你已掌握 Python 核心语法、文件操作、异常处理、OOP、模块组织和虚拟环境等关键技能。本篇将整合所有知识,带你从零开始开发一个功能完整、结构清晰、可维护的命令行 To-Do List(待办事项)应用。

你将实践:

  • 项目目录结构设计
  • 面向对象建模(TaskTaskManager
  • JSON 数据持久化
  • 命令行交互(argparse + 用户输入)
  • 异常处理与用户友好提示
  • 虚拟环境与依赖管理

1. 项目目标与功能清单

✅ 最终应用支持以下操作:

bash 复制代码
# 添加任务
python todo.py add "买牛奶"

# 列出所有任务
python todo.py list

# 标记任务完成
python todo.py done 1

# 删除任务
python todo.py remove 2

# 显示帮助
python todo.py --help

数据存储格式(tasks.json):

json 复制代码
[
  {
    "id": 1,
    "title": "买牛奶",
    "completed": false,
    "created_at": "2025-12-18T16:30:00"
  }
]

2. 项目结构设计

遵循前文最佳实践:

复制代码
todo-cli/
├── venv/                     ← 虚拟环境(不提交)
├── requirements.txt
├── .gitignore
├── README.md
├── todo.py                   ← 主程序入口
└── todolib/                  ← 核心功能包
    ├── __init__.py
    ├── task.py               ← Task 类
    ├── storage.py            ← 文件读写
    └── manager.py            ← 任务管理逻辑

3. 步骤一:创建虚拟环境与依赖

bash 复制代码
mkdir todo-cli && cd todo-cli
python -m venv venv
source venv/bin/activate      # Linux/Mac
# venv\Scripts\activate       # Windows

# 目前无第三方依赖,但保留文件
echo "# 项目依赖" > requirements.txt

💡 本项目仅使用标准库,无需安装额外包!


4. 步骤二:定义 Task 类(todolib/task.py

python 复制代码
from datetime import datetime
from typing import Dict

class Task:
    def __init__(self, title: str, completed: bool = False, created_at: str = None):
        self.title = title
        self.completed = completed
        self.created_at = created_at or datetime.now().isoformat()
        self.id = None  # 将由 TaskManager 分配

    def to_dict(self) -> Dict:
        return {
            "id": self.id,
            "title": self.title,
            "completed": self.completed,
            "created_at": self.created_at
        }

    @classmethod
    def from_dict(cls, data: Dict) -> "Task":
        task = cls(data["title"], data["completed"], data["created_at"])
        task.id = data["id"]
        return task

    def __str__(self):
        status = "✓" if self.completed else "○"
        return f"[{status}] {self.title}"

✅ 使用类型提示(typing)提升可读性

✅ 支持序列化(to_dict)与反序列化(from_dict


5. 步骤三:实现数据存储(todolib/storage.py

python 复制代码
import json
from pathlib import Path
from typing import List, Dict
from .task import Task

TASKS_FILE = Path("tasks.json")

def load_tasks() -> List[Task]:
    if not TASKS_FILE.exists():
        return []
    try:
        data = json.loads(TASKS_FILE.read_text(encoding="utf-8"))
        return [Task.from_dict(item) for item in data]
    except (json.JSONDecodeError, KeyError) as e:
        print(f"⚠️  警告:任务文件损坏,已重置。错误: {e}")
        return []

def save_tasks(tasks: List[Task]) -> None:
    data = [task.to_dict() for task in tasks]
    TASKS_FILE.write_text(
        json.dumps(data, ensure_ascii=False, indent=2),
        encoding="utf-8"
    )

🔒 安全读取:捕获 JSON 解析错误,防止程序崩溃


6. 步骤四:任务管理逻辑(todolib/manager.py

python 复制代码
from typing import List
from .task import Task
from .storage import load_tasks, save_tasks

class TaskManager:
    def __init__(self):
        self.tasks: List[Task] = load_tasks()
        self._next_id = max((t.id for t in self.tasks), default=0) + 1

    def add_task(self, title: str) -> Task:
        task = Task(title)
        task.id = self._next_id
        self._next_id += 1
        self.tasks.append(task)
        save_tasks(self.tasks)
        return task

    def get_task_by_id(self, task_id: int) -> Task:
        for task in self.tasks:
            if task.id == task_id:
                return task
        raise ValueError(f"任务 ID {task_id} 不存在")

    def mark_done(self, task_id: int) -> None:
        task = self.get_task_by_id(task_id)
        task.completed = True
        save_tasks(self.tasks)

    def remove_task(self, task_id: int) -> None:
        task = self.get_task_by_id(task_id)
        self.tasks.remove(task)
        save_tasks(self.tasks)

    def list_tasks(self, show_completed: bool = True) -> List[Task]:
        if show_completed:
            return self.tasks
        return [t for t in self.tasks if not t.completed]

✅ 封装业务逻辑,主程序只需调用方法

✅ 自动分配唯一 ID,避免冲突


7. 步骤五:主程序入口(todo.py

python 复制代码
#!/usr/bin/env python3
import argparse
from todolib.manager import TaskManager

def main():
    parser = argparse.ArgumentParser(description="简易命令行 To-Do List")
    subparsers = parser.add_subparsers(dest="command", help="可用命令")

    # add
    add_parser = subparsers.add_parser("add", help="添加新任务")
    add_parser.add_argument("title", help="任务内容")

    # list
    list_parser = subparsers.add_parser("list", help="列出任务")
    list_parser.add_argument("--all", action="store_true", help="显示已完成任务")

    # done
    done_parser = subparsers.add_parser("done", help="标记任务完成")
    done_parser.add_argument("id", type=int, help="任务ID")

    # remove
    remove_parser = subparsers.add_parser("remove", help="删除任务")
    remove_parser.add_argument("id", type=int, help="任务ID")

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        return

    manager = TaskManager()

    try:
        if args.command == "add":
            task = manager.add_task(args.title)
            print(f"✅ 已添加任务 #{task.id}: {task.title}")

        elif args.command == "list":
            tasks = manager.list_tasks(show_completed=args.all)
            if not tasks:
                print("📭 暂无任务")
                return
            for task in tasks:
                print(f"#{task.id} {task}")

        elif args.command == "done":
            manager.mark_done(args.id)
            print(f"🎉 任务 #{args.id} 已完成!")

        elif args.command == "remove":
            manager.remove_task(args.id)
            print(f"🗑️  任务 #{args.id} 已删除")

    except ValueError as e:
        print(f"❌ 错误: {e}")
    except Exception as e:
        print(f"💥 未知错误: {e}")

if __name__ == "__main__":
    main()

✨ 用户友好提示(✅/❌/🎉 等 emoji 提升体验)

🛡️ 异常捕获:防止因无效 ID 导致崩溃


8. 测试你的应用

bash 复制代码
# 添加任务
python todo.py add "学习 Python"
python todo.py add "写博客"

# 查看任务
python todo.py list
# 输出:
# #1 [○] 学习 Python
# #2 [○] 写博客

# 完成任务
python todo.py done 1

# 再次查看(默认隐藏已完成)
python todo.py list
# 输出:
# #2 [○] 写博客

# 查看所有
python todo.py list --all

# 删除任务
python todo.py remove 2

💾 所有数据自动保存到 tasks.json,重启后依然存在!


9. 项目优化建议(进阶)

  1. 添加单元测试 (使用 unittestpytest
  2. 支持任务优先级或截止日期
  3. 实现"清空已完成"命令
  4. rich 库美化终端输出(需安装第三方包)
  5. 打包为可执行命令 (如 pip install -e .

10. 总结:你已掌握的核心能力

技能 在本项目中的体现
OOP 设计 TaskTaskManager 封装数据与行为
文件操作 storage.py 安全读写 JSON
异常处理 捕获无效 ID、文件损坏等错误
命令行交互 argparse 构建专业 CLI
模块化 功能拆分为 todolib/
虚拟环境 隔离开发环境

🎯 恭喜!你已具备独立开发小型 Python 应用的能力。


下一步方向

  1. 扩展功能:添加子任务、分类标签、搜索
  2. 学习 Web 开发:用 Flask/Django 将此应用转为网页版
  3. 探索自动化:用此工具管理你的学习计划
  4. 阅读开源项目 :如 todo-txt

🐍 编程不是学出来的,是写出来的。

从今天起,你不再只是学习者,而是创造者!

继续编码,用 Python 改变你的世界!

相关推荐
AI营销干货站2 小时前
原圈科技推动AI营销内容生产革新:高质量素材每日自动生成
人工智能·科技
jcsx2 小时前
如何将django项目发布为https
python·https·django
Elastic 中国社区官方博客2 小时前
Elasticsearch:你是说,用于混合搜索(hybrid search)
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
我不是程序猿儿2 小时前
【C#】ScottPlot的Refresh()
开发语言·c#
神州问学2 小时前
Gemini 3.0 科普:Google 这次把 AI 做成“能看懂世界的助手”了
人工智能
Neolnfra2 小时前
渗透测试标准化流程
开发语言·安全·web安全·http·网络安全·https·系统安全
计算机学姐2 小时前
基于php的旅游景点预约门票管理系统
开发语言·后端·mysql·php·phpstorm
AA陈超2 小时前
枚举类 `ETriggerEvent`
开发语言·c++·笔记·学习·ue5
LaughingZhu2 小时前
Product Hunt 每日热榜 | 2025-12-18
人工智能·经验分享·神经网络·搜索引擎·产品运营