欢迎回来!🎉 这是我们的最后一课!我们要把前面学的所有知识综合起来,创建一个完整的应用程序!
前十一节课我们学会了: - 变量和基本数据类型 - 条件判断和循环 - 列表和字典 - 函数(代码的魔法师) - 文件操作(数据持久化) - 模块和包(代码组织) - 面向对象编程(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
恭喜你!🎉 你已经完成了整个Python教程!
亲爱的Python学习者:
恭喜你完成了这12课的Python教程!🎉
从一开始的"Hello, World!",到现在能够创建一个完整的应用程序,你已经走了很长的路。
编程的道路上没有终点,只有不断的学习和探索。
记住:最好的学习方式是动手实践。
不要害怕犯错,错误是最好的老师。每一个bug都是你成长的机会。
保持好奇心,享受编程的乐趣!
你已经是一个Python程序员了! 🎉
祝你编程愉快!未来可期! 💪🚀
全文完