A final project is built in small, useful steps.
Until now, you practiced individual Python and OOP ideas. In the final project, you will combine them in one local application: a Task Manager for keeping small tasks organized.
task_manager/
├── main.py
├── models/
│ ├── __init__.py
│ └── task.py
└── services/
├── __init__.py
└── task_manager.pymodels contains data objects. services contains code that coordinates those objects. main.py starts a small demonstration. Later concepts will add task operations, JSON storage, and tests without moving these responsibilities around.
A Task represents one piece of data. A TaskManager owns a collection of Task objects and will later offer operations for that collection. This separation keeps one class from trying to do every job.
Stable project agreement: Each Task will have a string task_id, a title, and a completion state. Each TaskManager will own its own tasks list. Concept 29 adds management operations, concept 30 saves data as JSON, and concept 31 tests the rules.
class Task:
"""Represent one task in the local project."""
def __init__(self, task_id: str, title: str) -> None:
self.task_id = task_id
self.title = title
self.completed = False
class TaskManager:
"""Keep this manager instance's tasks together."""
def __init__(self) -> None:
self.tasks: list[Task] = []
def add_task(self, task: Task) -> None:
self.tasks.append(task)The constructor gives every TaskManager a new empty list. The list is an instance attribute, so adding a task to one manager does not add it to another manager. Type hints and short docstrings make the intended roles easier to read; they do not add automatic validation.
from models.task import Task
from services.task_manager import TaskManagerThe dot in an import follows the project folders. Empty __init__.py files make these folders clear classic Python packages for this beginner-friendly project.
from models.task import Task
from services.task_manager import TaskManager
first_manager = TaskManager()
second_manager = TaskManager()
first_manager.add_task(Task("task-1", "Read the project plan"))
print(len(first_manager.tasks))
print(len(second_manager.tasks))The output is 1 and then 0. The first manager received one Task; the second manager starts independently with no tasks.
How this project will grow
- Concept 28 — Build the foundationCreate the folders, Task, TaskManager, imports, and a small runnable demonstration.
- Concept 29 — Manage tasksBuild on add_task() with focused operations for viewing tasks, finding a task, changing its status, and removing it.
- Concept 30 — Save locallyAdd a small JSON storage module so task data can be saved and loaded from a local file.
- Concept 31 — Protect the rulesUse unittest to check the classes, manager behavior, and local storage.
Common project-structure mistakes
- One shared class listDo not write tasks = [] directly on TaskManager for this project. Put self.tasks = [] in __init__ so every manager starts separately.
- Putting every responsibility in main.pyKeep data in Task and collection behavior in TaskManager. main.py should demonstrate how the pieces work together.
- Building future features too earlyDo not add JSON files or a full command menu yet. Build and run this small foundation first.
