【Python実践プロジェクト】知識を総動員!ToDoリストアプリをゼロから作ろう (関数, クラス, ファイル保存) 🔨

「Pythonの基本は学んだけど、これを使って実際に何が作れるんだろう?」
「学んだ関数やクラス、制御構文をどう組み合わせればいいのか分からない…」
「自分だけの便利なツールを作って、プログラミングの達成感を味わいたい!」

こんにちは! Pythonプログラミング探検隊、隊長のPythonistaです! これまで私たちは、Pythonの文法から始まり、様々な標準ライブラリ、そしてNumPyPillowといった強力な外部ライブラリまで、長い冒険を続けてきましたね。個々の「武器」の使い方は、もうマスターしたことでしょう。

今回は、その集大成となる「実践プロジェクト編」です! これまで手に入れてきた数々の武器を組み合わせ、ゼロから一つのアプリケーション、コンソールベースの「シンプルなToDoリストアプリ」を作り上げていきます。この記事は、単にコードを書き写すチュートリアルではありません。問題をどう分割し、どの技術で解決し、どう改善していくかという、プログラミングの思考プロセスそのものを一緒に体験していくことを目指します。さあ、学んだ知識を「形」にする、最高にエキサイティングな開発を始めましょう!


1. 設計図を描こう:どんなToDoリストアプリを作る?

優れたプログラムは、優れた設計から生まれます。まずは、私たちが作るアプリが持つべき機能を洗い出してみましょう。

1.1. 機能要件

最低限、以下の機能を持つことを目標とします。

  1. タスクの追加 (Add): 新しいやるべきことをリストに追加できる。
  2. タスクの一覧表示 (List): 現在のタスクを番号付きで全て表示できる。
  3. タスクの完了 (Complete): 指定した番号のタスクを「完了済み」にできる。(フェーズ3で実装)
  4. タスクの削除 (Delete): 指定した番号のタスクをリストから削除できる。
  5. データの保存と読み込み (Persistence): プログラムを終了しても、タスクリストが消えないようにファイルに保存し、次回起動時に読み込める。(フェーズ2で実装)
  6. プログラムの終了 (Quit): アプリケーションを安全に終了できる。

1.2. 開発の進め方(3つのフェーズ)

一度に全てを作ろうとすると複雑になるので、段階的に機能を実装していきます。

  • フェーズ1:基本機能の実装 - まずはプログラム実行中だけ使える、基本的なタスク追加・表示・削除機能を作ります。
  • フェーズ2:データ永続化 - jsonモジュールを使い、タスクをファイルに保存・読み込みできるようにします。
  • フェーズ3:機能向上とリファクタリング - タスクに「完了」ステータスを持たせ、コードをクラスを使ってより整理された構造に改良(リファクタリング)します。

2. フェーズ1:基本機能の実装(インメモリ版)

まずは、プログラムが動いている間だけタスクを記憶する、最もシンプルなバージョンから作り始めます。

2.1. プログラムの骨格とメインループ

プログラムの基本的な構造は、「ユーザーに操作を選んでもらい、対応する処理を呼び出し、それを繰り返す」というものです。これはwhile True:ループで実現できます。

def show_menu():
    """ユーザーにメニューを表示する"""
    print("\n--- ToDoリスト メニュー ---")
    print("1. タスクを一覧表示")
    print("2. タスクを追加")
    print("3. タスクを削除")
    print("q. 終了")

def main():
    """メイン処理"""
    tasks = [] # タスクを保存するリスト(今はまだ空)

    while True:
        show_menu()
        choice = input("操作を選んでください: ")

        if choice == '1':
            # list_tasks(tasks) # 後で実装
            print("(一覧表示機能を実装予定)")
        elif choice == '2':
            # add_task(tasks) # 後で実装
            print("(追加機能を実装予定)")
        elif choice == '3':
            # delete_task(tasks) # 後で実装
            print("(削除機能を実装予定)")
        elif choice.lower() == 'q':
            print("アプリケーションを終了します。")
            break
        else:
            print("無効な選択です。もう一度お選びください。")

if __name__ == "__main__":
    main()

この骨格コードで、基本的なメニュー表示とユーザー入力の受付、終了処理ができます。

2.2. 各機能の関数を実装

次に、main関数の中のコメントアウト部分を、実際の機能を持つ関数で置き換えていきます。

# ... (show_menu関数は同じ) ...

def list_tasks(tasks):
    """タスクの一覧を表示する"""
    print("\n--- 現在のタスク ---")
    if not tasks:
        print("タスクはありません。")
        return
    for i, task in enumerate(tasks, 1):
        print(f"{i}. {task}")

def add_task(tasks):
    """新しいタスクを追加する"""
    task_description = input("追加するタスクの内容を入力してください: ")
    tasks.append(task_description)
    print(f"タスク「{task_description}」を追加しました。")

def delete_task(tasks):
    """タスクを削除する"""
    list_tasks(tasks)
    if not tasks:
        return
    
    try:
        task_num_str = input("削除するタスクの番号を入力してください: ")
        task_num = int(task_num_str)
        
        # リストのインデックスは0から始まるので、ユーザー入力から1を引く
        if 1 <= task_num <= len(tasks):
            removed_task = tasks.pop(task_num - 1)
            print(f"タスク「{removed_task}」を削除しました。")
        else:
            print("無効な番号です。")
            
    except ValueError:
        print("エラー: 数値を入力してください。")

# main関数のwhileループ内を、これらの関数呼び出しに書き換える
# if choice == '1': list_tasks(tasks)
# elif choice == '2': add_task(tasks)
# elif choice == '3': delete_task(tasks)
# ...

delete_task関数では、ユーザーの不正な入力(数字以外や範囲外の番号)に対応するため、早速try-exceptによるエラーハンドリングを使っています。これがプログラムを頑丈にする第一歩です。

【チェックポイント】 ここまでのコードを組み合わせると、プログラムを実行している間だけですが、ToDoリストとして完全に機能するはずです。まずここまでを完成させて、動かしてみましょう!


3. フェーズ2:データ永続化 (`json`モジュール)

現在のプログラムでは、終了するとタスクリストが消えてしまいます。これでは不便なので、jsonモジュールを使ってタスクリストをファイルに保存し、次回起動時に読み込めるようにします。

import json
import os # osモジュールも使う場合がある

FILE_NAME = 'todolist.json'

def load_tasks_from_file():
    """ファイルからタスクを読み込む"""
    try:
        with open(FILE_NAME, 'r', encoding='utf-8') as f:
            return json.load(f)
    except FileNotFoundError:
        # ファイルがまだ存在しない場合は空のリストを返す
        return []

def save_tasks_to_file(tasks):
    """タスクをファイルに保存する"""
    with open(FILE_NAME, 'w', encoding='utf-8') as f:
        json.dump(tasks, f, indent=4, ensure_ascii=False)

# main関数を以下のように変更
def main():
    tasks = load_tasks_from_file() # ★★★ 起動時にファイルを読み込む
    
    while True:
        show_menu()
        choice = input("操作を選んでください: ")
        
        if choice == '1':
            list_tasks(tasks)
        elif choice == '2':
            add_task(tasks)
            save_tasks_to_file(tasks) # ★★★ 変更があったらすぐに保存
        elif choice == '3':
            delete_task(tasks)
            save_tasks_to_file(tasks) # ★★★ 変更があったらすぐに保存
        elif choice.lower() == 'q':
            print("アプリケーションを終了します。")
            break
        else:
            print("無効な選択です。もう一度お選びください。")

起動時にload_tasks_from_file()を呼び出し、タスクが追加・削除されるたびにsave_tasks_to_file()を呼び出すことで、データの永続化が実現できました!


4. フェーズ3:機能向上とリファクタリング (クラスの導入)

現在のタスクはただの文字列ですが、これに「完了/未完了」のような状態を持たせたい場合、文字列だけでは管理が難しくなります。そこで、タスク一つ一つをオブジェクトとして扱うために**クラス**を導入します。

4.1. `Task`クラスの定義

タスクが持つべき情報(内容、状態)と、それに関する操作(完了にするなど)を`Task`クラスにまとめます。

class Task:
    def __init__(self, description, done=False):
        self.description = description
        self.done = done

    def mark_as_done(self):
        self.done = True
        
    def __str__(self):
        # オブジェクトをprintしたときの表示形式を定義
        status = "[完了]" if self.done else "[未完了]"
        return f"{status} {self.description}"

これで、タスクは単なる文字列ではなく、description(内容)とdone(完了状態)という2つの属性を持つオブジェクトになりました。

4.2. プログラム全体のリファクタリング

この`Task`クラスを使うように、これまでの関数を書き換えていきます(これをリファクタリングと言います)。例えば、add_taskは文字列ではなくTaskオブジェクトをリストに追加するように、list_tasksは各Taskオブジェクトの表示形式(__str__メソッドで定義済み)を使うように変更します。 さらに、新しい機能として「タスクを完了にする」complete_task関数を追加しましょう。

(リファクタリング後の完全なコードは長くなるため、ここでは主要な変更点を示します。ぜひご自身で書き換えることに挑戦してみてください!完成版のコードは記事の最後にまとめて掲載します。)

  • add_task: new_task = Task(description) のようにインスタンスを作成し、tasks.append(new_task)とする。
  • list_tasks: `print(f"{i}. {task}")` とするだけで、Taskクラスの__str__メソッドが呼ばれて綺麗に表示される。
  • 新しい関数complete_task(tasks)を追加: ユーザーに番号を選ばせ、tasks[task_num - 1].mark_as_done()のようにしてタスクを完了状態にする。
  • ファイル保存/読み込みの修正: クラスのオブジェクトは直接JSONに保存できないため、保存時は「辞書のリスト」に変換し、読み込み時は「辞書のリスト」から「`Task`オブジェクトのリスト」に復元する工夫が必要になります。
    # 保存時 (tasksはTaskオブジェクトのリスト)
            data_to_save = [{'description': t.description, 'done': t.done} for t in tasks]
            json.dump(data_to_save, f, ...)
    
    # 読み込み時
            data_from_file = json.load(f)
            tasks = [Task(item['description'], item['done']) for item in data_from_file]
            return tasks
            

ここに最終的な完成版コード全体を掲載


5. まとめ:あなただけのToDoリストアプリが完成!

お疲れ様でした!この実践プロジェクトを通して、私たちは単なるコードの断片ではなく、実際に機能する一つのアプリケーションを作り上げました。この過程で、

といった、プログラミングにおける非常に重要な概念を実践的に学ぶことができました。

完成したToDoリストアプリは、あなたのための最初の「作品」です。ぜひ、ここからさらにあなた自身のアイデアで改良を加えてみてください。「タスクに優先度を追加する」「締め切り日(datetime)を設定する」「カテゴリ分けできるようにする」など、可能性は無限大です。自分で考えてプログラムを作る楽しさを、ぜひこれからも追求していってください!

応援しています! Happy Coding! 👨‍💻👩‍💻

その他の投稿はこちら

コメント

このブログの人気の投稿

タイトルまとめ

これで迷わない!WindowsでPython環境構築する一番やさしい方法 #0

【Python標準ライブラリ完結!】11の冒険をありがとう!君のPython力が飛躍する「次の一歩」とは? 🚀