← 返回首页 | ← 返回教程列表

Python学习教程 - 第十二课:综合实战项目,打造你的应用!

欢迎回来!🎉 这是我们的最后一课!我们要把前面学的所有知识综合起来,创建一个完整的应用程序!


复习一下前十一节课

前十一节课我们学会了: - 变量和基本数据类型 - 条件判断和循环 - 列表和字典 - 函数(代码的魔法师) - 文件操作(数据持久化) - 模块和包(代码组织) - 面向对象编程(OOP) - 异常和错误处理 - 正则表达式 - 日期和时间处理 - 网络编程

这节课我们要把这些知识综合起来!


项目概述:任务管理系统

我们要创建一个功能完整的任务管理系统(Todo List),包含以下功能:

✅ 用户管理(注册、登录)
✅ 任务管理(添加、查看、编辑、删除)
✅ 任务分类和标签
✅ 任务优先级和截止日期
✅ 任务搜索和筛选
✅ 数据持久化(JSON文件存储)
✅ 命令行界面(CLI)


项目结构

todo_app/
├── __init__.py
├── models.py          # 数据模型
├── storage.py         # 数据存储
├── todo_app.py        # 主应用
└── cli.py             # 命令行界面

第一步:数据模型

# models.py
from datetime import datetime
from enum import Enum
import json

class Priority(Enum):
    """任务优先级"""
    LOW = 1
    MEDIUM = 2
    HIGH = 3
    URGENT = 4

    @classmethod
    def from_string(cls, s):
        """从字符串创建优先级"""
        s = s.lower().strip()
        mapping = {
            'low': cls.LOW,
            'medium': cls.MEDIUM,
            'high': cls.HIGH,
            'urgent': cls.URGENT,
            '1': cls.LOW,
            '2': cls.MEDIUM,
            '3': cls.HIGH,
            '4': cls.URGENT
        }
        return mapping.get(s, cls.MEDIUM)

    def __str__(self):
        return self.name.lower()

class TaskStatus(Enum):
    """任务状态"""
    TODO = "todo"
    IN_PROGRESS = "in_progress"
    DONE = "done"

    @classmethod
    def from_string(cls, s):
        s = s.lower().strip()
        mapping = {
            'todo': cls.TODO,
            'pending': cls.TODO,
            'in_progress': cls.IN_PROGRESS,
            'doing': cls.IN_PROGRESS,
            'done': cls.DONE,
            'completed': cls.DONE,
            'finished': cls.DONE
        }
        return mapping.get(s, cls.TODO)

    def __str__(self):
        return self.value

class Task:
    """任务类"""
    def __init__(self, title, description="", priority=Priority.MEDIUM, 
                 status=TaskStatus.TODO, due_date=None, tags=None):
        self.id = None
        self.title = title
        self.description = description
        self.priority = priority
        self.status = status
        self.due_date = due_date
        self.tags = tags or []
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        self.completed_at = None

    def mark_done(self):
        """标记为完成"""
        self.status = TaskStatus.DONE
        self.completed_at = datetime.now()
        self.updated_at = datetime.now()

    def mark_in_progress(self):
        """标记为进行中"""
        self.status = TaskStatus.IN_PROGRESS
        self.updated_at = datetime.now()

    def is_overdue(self):
        """检查是否逾期"""
        if self.due_date and self.status != TaskStatus.DONE:
            return datetime.now() > self.due_date
        return False

    def to_dict(self):
        """转换为字典"""
        return {
            'id': self.id,
            'title': self.title,
            'description': self.description,
            'priority': self.priority.value,
            'status': self.status.value,
            'due_date': self.due_date.isoformat() if self.due_date else None,
            'tags': self.tags,
            'created_at': self.created_at.isoformat(),
            'updated_at': self.updated_at.isoformat(),
            'completed_at': self.completed_at.isoformat() if self.completed_at else None
        }

    @classmethod
    def from_dict(cls, data):
        """从字典创建"""
        task = cls(
            title=data['title'],
            description=data.get('description', ''),
            priority=Priority(data['priority']),
            status=TaskStatus(data['status']),
            due_date=datetime.fromisoformat(data['due_date']) if data.get('due_date') else None,
            tags=data.get('tags', [])
        )
        task.id = data['id']
        task.created_at = datetime.fromisoformat(data['created_at'])
        task.updated_at = datetime.fromisoformat(data['updated_at'])
        if data.get('completed_at'):
            task.completed_at = datetime.fromisoformat(data['completed_at'])
        return task

    def __str__(self):
        priority_emoji = {
            Priority.LOW: '🟢',
            Priority.MEDIUM: '🟡',
            Priority.HIGH: '🟠',
            Priority.URGENT: '🔴'
        }

        status_emoji = {
            TaskStatus.TODO: '📋',
            TaskStatus.IN_PROGRESS: '🚀',
            TaskStatus.DONE: '✅'
        }

        overdue = " ⚠️ 已逾期" if self.is_overdue() else ""

        return f"{priority_emoji[self.priority]} {status_emoji[self.status]} #{self.id} {self.title}{overdue}"

class User:
    """用户类"""
    def __init__(self, username, password_hash):
        self.username = username
        self.password_hash = password_hash
        self.created_at = datetime.now()
        self.tasks = []
        self._task_id_counter = 1

    def add_task(self, task):
        """添加任务"""
        task.id = self._task_id_counter
        self._task_id_counter += 1
        self.tasks.append(task)
        return task

    def get_task(self, task_id):
        """获取任务"""
        for task in self.tasks:
            if task.id == task_id:
                return task
        return None

    def delete_task(self, task_id):
        """删除任务"""
        task = self.get_task(task_id)
        if task:
            self.tasks.remove(task)
            return True
        return False

    def get_tasks_by_status(self, status):
        """按状态获取任务"""
        return [t for t in self.tasks if t.status == status]

    def get_tasks_by_tag(self, tag):
        """按标签获取任务"""
        return [t for t in self.tasks if tag in t.tags]

    def search_tasks(self, keyword):
        """搜索任务"""
        keyword = keyword.lower()
        return [t for t in self.tasks 
                if keyword in t.title.lower() 
                or keyword in t.description.lower()]

    def to_dict(self):
        """转换为字典"""
        return {
            'username': self.username,
            'password_hash': self.password_hash,
            'created_at': self.created_at.isoformat(),
            'tasks': [task.to_dict() for task in self.tasks],
            '_task_id_counter': self._task_id_counter
        }

    @classmethod
    def from_dict(cls, data):
        """从字典创建"""
        user = cls(
            username=data['username'],
            password_hash=data['password_hash']
        )
        user.created_at = datetime.fromisoformat(data['created_at'])
        user.tasks = [Task.from_dict(t) for t in data['tasks']]
        user._task_id_counter = data['_task_id_counter']
        return user

# storage.py
import json
import hashlib
from pathlib import Path
from models import User

class Storage:
    """数据存储类"""
    def __init__(self, data_dir="data"):
        self.data_dir = Path(data_dir)
        self.data_dir.mkdir(exist_ok=True)
        self.users_file = self.data_dir / "users.json"
        self.users = {}
        self.load()

    def hash_password(self, password):
        """简单的密码哈希(生产环境请使用bcrypt)"""
        return hashlib.sha256(password.encode()).hexdigest()

    def register(self, username, password):
        """注册用户"""
        if username in self.users:
            return False, "用户名已存在"

        if len(username) < 3:
            return False, "用户名至少3个字符"

        if len(password) < 6:
            return False, "密码至少6个字符"

        user = User(username, self.hash_password(password))
        self.users[username] = user
        self.save()
        return True, "注册成功"

    def login(self, username, password):
        """登录"""
        if username not in self.users:
            return None, "用户名或密码错误"

        user = self.users[username]
        if user.password_hash != self.hash_password(password):
            return None, "用户名或密码错误"

        return user, "登录成功"

    def save(self):
        """保存数据"""
        data = {
            username: user.to_dict() 
            for username, user in self.users.items()
        }

        with open(self.users_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

    def load(self):
        """加载数据"""
        if self.users_file.exists():
            with open(self.users_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
                self.users = {
                    username: User.from_dict(user_data)
                    for username, user_data in data.items()
                }

# todo_app.py
from datetime import datetime
from models import Task, Priority, TaskStatus
from storage import Storage

class TodoApp:
    """任务管理应用"""
    def __init__(self):
        self.storage = Storage()
        self.current_user = None

    def register(self, username, password):
        """注册"""
        success, message = self.storage.register(username, password)
        return success, message

    def login(self, username, password):
        """登录"""
        user, message = self.storage.login(username, password)
        if user:
            self.current_user = user
        return user is not None, message

    def logout(self):
        """登出"""
        self.current_user = None

    def add_task(self, title, description="", priority="medium", 
                 due_date_str=None, tags=None):
        """添加任务"""
        if not self.current_user:
            return None, "请先登录"

        due_date = None
        if due_date_str:
            try:
                due_date = datetime.strptime(due_date_str, "%Y-%m-%d")
            except ValueError:
                return None, "日期格式错误,请使用 YYYY-MM-DD"

        task = Task(
            title=title,
            description=description,
            priority=Priority.from_string(priority),
            due_date=due_date,
            tags=tags or []
        )

        self.current_user.add_task(task)
        self.storage.save()
        return task, "任务添加成功"

    def update_task(self, task_id, **kwargs):
        """更新任务"""
        if not self.current_user:
            return False, "请先登录"

        task = self.current_user.get_task(task_id)
        if not task:
            return False, "任务不存在"

        if 'title' in kwargs:
            task.title = kwargs['title']
        if 'description' in kwargs:
            task.description = kwargs['description']
        if 'priority' in kwargs:
            task.priority = Priority.from_string(kwargs['priority'])
        if 'status' in kwargs:
            task.status = TaskStatus.from_string(kwargs['status'])
        if 'due_date_str' in kwargs:
            if kwargs['due_date_str']:
                task.due_date = datetime.strptime(kwargs['due_date_str'], "%Y-%m-%d")
            else:
                task.due_date = None
        if 'tags' in kwargs:
            task.tags = kwargs['tags']

        task.updated_at = datetime.now()
        self.storage.save()
        return True, "任务更新成功"

    def delete_task(self, task_id):
        """删除任务"""
        if not self.current_user:
            return False, "请先登录"

        success = self.current_user.delete_task(task_id)
        if success:
            self.storage.save()
            return True, "任务删除成功"
        return False, "任务不存在"

    def mark_task_done(self, task_id):
        """标记任务完成"""
        if not self.current_user:
            return False, "请先登录"

        task = self.current_user.get_task(task_id)
        if not task:
            return False, "任务不存在"

        task.mark_done()
        self.storage.save()
        return True, "任务已标记为完成"

    def get_all_tasks(self):
        """获取所有任务"""
        if not self.current_user:
            return []
        return sorted(self.current_user.tasks, key=lambda t: t.created_at, reverse=True)

    def get_tasks_by_status(self, status):
        """按状态获取任务"""
        if not self.current_user:
            return []
        status_enum = TaskStatus.from_string(status)
        return self.current_user.get_tasks_by_status(status_enum)

    def search_tasks(self, keyword):
        """搜索任务"""
        if not self.current_user:
            return []
        return self.current_user.search_tasks(keyword)

    def get_statistics(self):
        """获取统计信息"""
        if not self.current_user:
            return None

        tasks = self.current_user.tasks
        total = len(tasks)
        todo = len([t for t in tasks if t.status == TaskStatus.TODO])
        in_progress = len([t for t in tasks if t.status == TaskStatus.IN_PROGRESS])
        done = len([t for t in tasks if t.status == TaskStatus.DONE])
        overdue = len([t for t in tasks if t.is_overdue()])

        return {
            'total': total,
            'todo': todo,
            'in_progress': in_progress,
            'done': done,
            'overdue': overdue
        }

# cli.py
import sys
from todo_app import TodoApp
from models import TaskStatus

class CLI:
    """命令行界面"""
    def __init__(self):
        self.app = TodoApp()
        self.running = True

    def print_header(self):
        """打印标题"""
        print("\n" + "="*60)
        print("  📋 任务管理系统")
        print("="*60)

    def print_menu(self):
        """打印菜单"""
        if not self.app.current_user:
            print("\n请选择:")
            print("  1. 注册")
            print("  2. 登录")
            print("  0. 退出")
        else:
            print(f"\n👤 当前用户: {self.app.current_user.username}")
            print("\n请选择:")
            print("  1. 查看所有任务")
            print("  2. 添加任务")
            print("  3. 编辑任务")
            print("  4. 删除任务")
            print("  5. 标记任务完成")
            print("  6. 搜索任务")
            print("  7. 查看统计")
            print("  8. 登出")
            print("  0. 退出")

    def register(self):
        """注册"""
        print("\n--- 用户注册 ---")
        username = input("用户名: ").strip()
        password = input("密码: ").strip()
        confirm_password = input("确认密码: ").strip()

        if password != confirm_password:
            print("❌ 两次密码不一致")
            return

        success, message = self.app.register(username, password)
        if success:
            print(f"✅ {message}")
        else:
            print(f"❌ {message}")

    def login(self):
        """登录"""
        print("\n--- 用户登录 ---")
        username = input("用户名: ").strip()
        password = input("密码: ").strip()

        success, message = self.app.login(username, password)
        if success:
            print(f"✅ {message}")
        else:
            print(f"❌ {message}")

    def list_tasks(self):
        """列出任务"""
        tasks = self.app.get_all_tasks()

        if not tasks:
            print("\n📭 暂无任务")
            return

        print(f"\n📋 所有任务 ({len(tasks)}个):")
        print("-"*60)
        for task in tasks:
            print(f"\n{task}")
            if task.description:
                print(f"   描述: {task.description}")
            if task.due_date:
                print(f"   截止: {task.due_date.strftime('%Y-%m-%d')}")
            if task.tags:
                print(f"   标签: {', '.join(task.tags)}")
        print("\n" + "-"*60)

    def add_task(self):
        """添加任务"""
        print("\n--- 添加任务 ---")
        title = input("标题: ").strip()
        if not title:
            print("❌ 标题不能为空")
            return

        description = input("描述 (可选): ").strip()
        priority = input("优先级 (low/medium/high/urgent, 默认medium): ").strip() or "medium"
        due_date = input("截止日期 (YYYY-MM-DD, 可选): ").strip() or None
        tags_str = input("标签 (逗号分隔, 可选): ").strip()
        tags = [t.strip() for t in tags_str.split(',')] if tags_str else []

        task, message = self.app.add_task(
            title=title,
            description=description,
            priority=priority,
            due_date_str=due_date,
            tags=tags
        )

        if task:
            print(f"✅ {message}")
        else:
            print(f"❌ {message}")

    def update_task(self):
        """更新任务"""
        print("\n--- 编辑任务 ---")
        task_id = input("任务ID: ").strip()
        if not task_id.isdigit():
            print("❌ 无效的ID")
            return

        task_id = int(task_id)

        title = input("新标题 (留空不修改): ").strip()
        description = input("新描述 (留空不修改): ").strip()
        priority = input("新优先级 (留空不修改): ").strip()
        status = input("新状态 (todo/in_progress/done, 留空不修改): ").strip()
        due_date = input("新截止日期 (留空不修改): ").strip()
        tags_str = input("新标签 (留空不修改): ").strip()

        kwargs = {}
        if title:
            kwargs['title'] = title
        if description:
            kwargs['description'] = description
        if priority:
            kwargs['priority'] = priority
        if status:
            kwargs['status'] = status
        if due_date:
            kwargs['due_date_str'] = due_date
        if tags_str:
            kwargs['tags'] = [t.strip() for t in tags_str.split(',')]

        if not kwargs:
            print("❌ 没有修改任何内容")
            return

        success, message = self.app.update_task(task_id, **kwargs)
        if success:
            print(f"✅ {message}")
        else:
            print(f"❌ {message}")

    def delete_task(self):
        """删除任务"""
        print("\n--- 删除任务 ---")
        task_id = input("任务ID: ").strip()
        if not task_id.isdigit():
            print("❌ 无效的ID")
            return

        task_id = int(task_id)
        confirm = input(f"确定要删除任务 #{task_id} 吗? (y/n): ").strip().lower()

        if confirm == 'y':
            success, message = self.app.delete_task(task_id)
            if success:
                print(f"✅ {message}")
            else:
                print(f"❌ {message}")
        else:
            print("已取消")

    def mark_done(self):
        """标记完成"""
        print("\n--- 标记任务完成 ---")
        task_id = input("任务ID: ").strip()
        if not task_id.isdigit():
            print("❌ 无效的ID")
            return

        task_id = int(task_id)
        success, message = self.app.mark_task_done(task_id)
        if success:
            print(f"✅ {message}")
        else:
            print(f"❌ {message}")

    def search_tasks(self):
        """搜索任务"""
        print("\n--- 搜索任务 ---")
        keyword = input("搜索关键词: ").strip()
        if not keyword:
            print("❌ 关键词不能为空")
            return

        tasks = self.app.search_tasks(keyword)
        if not tasks:
            print(f"📭 没有找到包含 '{keyword}' 的任务")
            return

        print(f"\n🔍 找到 {len(tasks)} 个任务:")
        for task in tasks:
            print(f"   {task}")

    def show_statistics(self):
        """显示统计"""
        stats = self.app.get_statistics()
        if not stats:
            return

        print("\n📊 统计信息:")
        print("-"*40)
        print(f"  总任务数: {stats['total']}")
        print(f"  待办: {stats['todo']}")
        print(f"  进行中: {stats['in_progress']}")
        print(f"  已完成: {stats['done']}")
        print(f"  已逾期: {stats['overdue']}")
        print("-"*40)

        if stats['total'] > 0:
            progress = stats['done'] / stats['total'] * 100
            print(f"  完成进度: {progress:.1f}%")

    def run(self):
        """运行应用"""
        self.print_header()

        while self.running:
            self.print_menu()
            choice = input("\n请选择: ").strip()

            if not self.app.current_user:
                if choice == '1':
                    self.register()
                elif choice == '2':
                    self.login()
                elif choice == '0':
                    self.running = False
                    print("\n👋 再见!")
                else:
                    print("❌ 无效的选择")
            else:
                if choice == '1':
                    self.list_tasks()
                elif choice == '2':
                    self.add_task()
                elif choice == '3':
                    self.update_task()
                elif choice == '4':
                    self.delete_task()
                elif choice == '5':
                    self.mark_done()
                elif choice == '6':
                    self.search_tasks()
                elif choice == '7':
                    self.show_statistics()
                elif choice == '8':
                    self.app.logout()
                    print("👋 已登出")
                elif choice == '0':
                    self.running = False
                    print("\n👋 再见!")
                else:
                    print("❌ 无效的选择")

# main.py
from cli import CLI

if __name__ == "__main__":
    app = CLI()
    app.run()

---

## 项目使用说明

### 运行项目

```bash
# 将代码保存到以下文件:
# - models.py
# - storage.py
# - todo_app.py
# - cli.py
# - main.py

# 然后运行
python main.py

功能演示

  1. 注册用户 → 输入用户名和密码
  2. 登录 → 使用注册的账号登录
  3. 添加任务 → 输入标题、描述、优先级等
  4. 查看任务 → 列出所有任务
  5. 标记完成 → 将任务标记为已完成
  6. 搜索任务 → 根据关键词搜索
  7. 查看统计 → 查看任务统计信息

第十二课小结

恭喜你!🎉 你已经完成了整个Python教程!

我们学过的内容:

  1. Hello, Python! - 变量和数据类型
  2. 做出判断! - 条件判断和循环
  3. 列表和字典! - 数据结构
  4. 函数,代码的魔法师! - 函数
  5. 文件操作,让数据持久化! - 文件操作
  6. 模块和包,代码的组织大师! - 模块和包
  7. 面向对象编程,创建你的世界! - OOP
  8. 异常和错误处理,让程序更健壮! - 异常处理
  9. 正则表达式,文本处理的瑞士军刀! - 正则表达式
  10. 日期和时间处理,掌控时间的魔法! - 日期时间
  11. 网络编程,连接世界的桥梁! - 网络编程
  12. 综合实战项目,打造你的应用! - 实战项目

毕业祝福 🎓

亲爱的Python学习者:

恭喜你完成了这12课的Python教程!🎉

从一开始的"Hello, World!",到现在能够创建一个完整的应用程序,你已经走了很长的路。

编程的道路上没有终点,只有不断的学习和探索。

接下来可以做什么?

  1. 继续实践:多写代码,多做项目
  2. 学习进阶:学习更多Python库和框架
  3. 参与开源:为开源项目贡献代码
  4. 分享知识:教别人,也是巩固自己
  5. 保持好奇:技术在不断发展,保持学习的热情

推荐的学习资源


最后的话

记住:最好的学习方式是动手实践。

不要害怕犯错,错误是最好的老师。每一个bug都是你成长的机会。

保持好奇心,享受编程的乐趣!

你已经是一个Python程序员了! 🎉


祝你编程愉快!未来可期! 💪🚀


全文完