Python | 2週間で身につく、アプリを作りながら学ぶPythonの基本 - 10日目

Python Python
スポンサーリンク

10日目のゴールとテーマ

10日目のテーマは「メニュー付きの“ちゃんとしたコンソールアプリ”を組み立てる」です。
ここまでで、関数・リスト・辞書・ファイル・例外・標準ライブラリと、かなりのパーツを学んできました。

今日はそれらを「バラバラの練習」ではなく、
ひとつのアプリとしてまとめ上げる感覚をつかんでもらいます。

目標はこうです。

メニューを表示して、ユーザーに「やりたいこと」を選んでもらう
選ばれた番号に応じて、対応する処理を呼び出す
アプリ全体の“流れ”を意識してコードを組み立てる

テーマアプリは、これまで何度か出てきた「タスク管理アプリ」を、
メニュー付きの“それっぽい形”に進化させます。


メニュー付きアプリの基本イメージ

「やりたいことを番号で選ぶ」スタイル

まずは、頭の中にイメージを作りましょう。
コンソールで動くアプリに、こんなメニューを出したいとします。

=== タスク管理アプリ ===
1: タスクを追加する
2: タスク一覧を表示する
3: タスクを保存する
4: タスクを読み込む
0: 終了する
番号を選んでください:

ユーザーは 1〜4 または 0 を入力します。
アプリは、その番号に応じて処理を切り替えます。

この「メニュー → 入力 → 分岐 → 処理 → またメニュー」というループが、
コンソールアプリの基本パターンです。


メニューを表示する関数を作る

「表示だけを担当する関数」に切り出す

まずは、メニュー表示を関数にしてしまいましょう。

def print_menu():
    print("=== タスク管理アプリ 10日目バージョン ===")
    print("1: タスクを追加する")
    print("2: タスク一覧を表示する")
    print("3: タスクを保存する")
    print("4: タスクを読み込む")
    print("0: 終了する")
Python

この関数は「メニューを表示するだけ」です。
ここに「入力」や「処理」は書きません。

こうやって「役割を1つに絞った関数」を作ると、
あとからメニューの文言を変えたいときも、この関数だけ見ればよくなります。


メニュー番号を入力させる関数

入力バリデーションを入れて「変な番号」を弾く

次に、「メニュー番号を入力してもらう」関数を作ります。
ここで、8日目にやった「入力バリデーション」の考え方を使います。

def input_menu_number():
    while True:
        text = input("番号を選んでください: ")

        try:
            value = int(text)
        except ValueError:
            print("数字で入力してください。")
            continue

        if value not in [0, 1, 2, 3, 4]:
            print("0〜4の番号を入力してください。")
            continue

        return value
Python

流れを丁寧に追います。

まず、while True で「正しい番号が入るまでくり返す」ループを作る。
毎回、文字列として入力を受け取る。
int に変換を試みて、数字でなければ「数字で入力してください」と言ってやり直し。
数字だったら、0〜4のどれかかどうかをチェックする。
範囲外なら「0〜4の番号を」と言ってやり直し。
問題なければ、その番号を return して関数を終わる。

これで、「変な入力でアプリが落ちる」ことはなくなります。
メニュー番号の入力は、アプリの“入口”なので、ここを丁寧にしておくと全体が安定します。


タスクのデータ構造を決める

「1件分は辞書」「全体はリスト」のおさらい

タスク管理アプリの「タスク1件分」は、こういう情報を持たせることにします。

タスク名(文字列)
締切日(datetime.date)

これを辞書で表現します。

task = {
    "name": "レポートを書く",
    "deadline": datetime.date(2026, 4, 10)
}
Python

複数のタスクは、これをリストに入れて管理します。

tasks = [
    {"name": "レポートを書く", "deadline": datetime.date(2026, 4, 10)},
    {"name": "買い物に行く", "deadline": datetime.date(2026, 3, 30)}
]
Python

この「辞書のリスト」は、5〜9日目で何度も出てきた形です。
もうかなり馴染んできたはずです。


タスクを追加する関数

入力 → バリデーション → 辞書にしてリストへ

タスクを1件追加する処理を、関数にします。
ここでは、9日目で作った parse_date の考え方も使います。

import datetime


def parse_date(text):
    try:
        year_str, month_str, day_str = text.split("-")
        year = int(year_str)
        month = int(month_str)
        day = int(day_str)
        return datetime.date(year, month, day)
    except Exception:
        return None


def add_task(tasks):
    name = input("タスク名を入力してください: ")

    if name == "":
        print("タスク名が空です。追加をキャンセルします。")
        return

    date_text = input("締切日を入力してください(YYYY-MM-DD): ")
    deadline = parse_date(date_text)

    if deadline is None:
        print("日付の形式が正しくありません。タスクの追加をキャンセルします。")
        return

    task = {"name": name, "deadline": deadline}
    tasks.append(task)

    print("タスクを追加しました。")
Python

ここでの重要ポイントを深掘りします。

add_task は「tasks リストを受け取って、その中に1件追加する」関数。
タスク名が空だったら、「追加しない」という判断をして return している。
締切日がパースできなかった場合も、「追加しない」という判断をしている。
成功したときだけ、辞書を作って tasks.append(task) している。

このように、「どんなときに追加するか」「どんなときに追加しないか」を
コードの中でハッキリさせておくと、アプリの挙動が読みやすくなります。


タスク一覧を表示する関数

今日との日数差も一緒に出してあげる

タスク一覧を表示する関数を作ります。
9日目の「締切まであと何日か」を再利用します。

def print_tasks(tasks):
    today = datetime.date.today()

    print("=== タスク一覧 ===")

    if len(tasks) == 0:
        print("タスクは登録されていません。")
        return

    for i, task in enumerate(tasks, start=1):
        name = task["name"]
        deadline = task["deadline"]

        diff = deadline - today
        days = diff.days

        deadline_text = deadline.strftime("%Y/%m/%d")

        if days > 0:
            status = "締切まであと " + str(days) + " 日"
        elif days == 0:
            status = "今日が締切!"
        else:
            status = "期限切れ(" + str(-days) + " 日過ぎ)"

        print(str(i) + ". " + name + "(締切: " + deadline_text + ") → " + status)
Python

ここでのポイントは、

enumerate(tasks, start=1) を使って、「1番、2番…」と番号付きで表示していること。
締切日と今日の日付の差から、「あと何日か」「期限切れか」を計算していること。
タスクが1件もない場合は、早めに return していること。

この関数は「表示だけ」を担当します。
削除や編集は、今日はやりませんが、
番号付きで表示しておくと、あとから「3番を削除」などの機能を足しやすくなります。


タスクをファイルに保存・読み込みする関数

7日目・9日目の内容を「タスク用」に再利用する

タスクをファイルに保存する関数と、読み込む関数を用意します。
形式はシンプルに、「1行1タスク」「カンマ区切り」でいきます。

def save_tasks_to_file(tasks, filename):
    with open(filename, "w", encoding="utf-8") as file:
        for task in tasks:
            name = task["name"]
            deadline = task["deadline"]
            line = name + "," + deadline.isoformat() + "\n"
            file.write(line)

    print("タスクをファイルに保存しました。")


def load_tasks_from_file(filename):
    tasks = []

    try:
        with open(filename, "r", encoding="utf-8") as file:
            for line in file:
                line = line.strip()
                if line == "":
                    continue

                parts = line.split(",")
                if len(parts) != 2:
                    continue

                name = parts[0]
                date_text = parts[1]

                deadline = parse_date(date_text)
                if deadline is None:
                    continue

                task = {"name": name, "deadline": deadline}
                tasks.append(task)

        print("タスクをファイルから読み込みました。")

    except FileNotFoundError:
        print("ファイルが見つかりませんでした。新しいタスク一覧を作ります。")
        tasks = []

    return tasks
Python

ここでの重要ポイントを整理します。

保存側では、deadline.isoformat() を使って日付を "YYYY-MM-DD" 形式の文字列にしている。
読み込み側では、その文字列を parse_date で date 型に戻している。
ファイルがない場合はエラーにせず、「空のタスク一覧」として扱っている。
おかしな行(カンマがない、日付がおかしいなど)はスキップしている。

これで、「アプリを終了してもタスクが残る」状態になります。


メインループを組み立てる

「メニュー → 入力 → 分岐 → 処理 → 戻る」の流れ

ここまで作った部品を、いよいよ「メインループ」に組み込みます。

FILENAME = "tasks.txt"


def main():
    tasks = load_tasks_from_file(FILENAME)

    while True:
        print_menu()
        number = input_menu_number()

        if number == 0:
            print("アプリを終了します。")
            break
        elif number == 1:
            add_task(tasks)
        elif number == 2:
            print_tasks(tasks)
        elif number == 3:
            save_tasks_to_file(tasks, FILENAME)
        elif number == 4:
            tasks = load_tasks_from_file(FILENAME)
        else:
            print("想定外の番号です。")


if __name__ == "__main__":
    main()
Python

この部分が、今日の一番おいしいところです。
流れを言葉で追ってみましょう。

最初に load_tasks_from_file で、前回までのタスクを読み込む。
while True で「メニューを何度でも表示する」ループに入る。
毎回 print_menu でメニューを表示し、input_menu_number で番号を入力してもらう。
番号に応じて、if / elif で処理を分岐する。
0 なら break でループを抜けて終了。
1 なら add_task でタスクを追加。
2 なら print_tasks で一覧表示。
3 なら save_tasks_to_file で保存。
4 なら load_tasks_from_file で再読み込み(外でファイルを編集した場合などを想定)。

そして、最後の

if __name__ == "__main__":
    main()
Python

は、「このファイルが“直接実行されたときだけ” main() を呼ぶ」というお約束です。
将来、このファイルを他のファイルから import して使うときに、
勝手に main が動き出さないようにするための仕組みです。

今の段階では、「Pythonのファイルの最後にこう書いておくと、
“ここがスタート地点です”と決められる」と理解しておけば十分です。


10日目で一番大事な感覚

「コードが“アプリの流れ”として読めるようになる」

今日あなたに持ってほしい感覚はこれです。

メニューを表示する関数
番号を入力させる関数
タスクを追加する関数
タスクを表示する関数
タスクを保存・読み込みする関数
それらを呼び出す main 関数

こうやって、「役割ごとに関数を分けておいて、
メインループで“物語”としてつなぐ」という感覚です。

コードを上から読んだときに、

起動したら前回のタスクを読み込んで
メニューを出して
ユーザーの選択に応じて処理して
0 が選ばれたら終わる

というストーリーが見えるかどうか。
ここまで来ると、あなたはもう「文法を知っている人」ではなく、
「アプリの流れを設計できる人」の側に足を踏み入れています。


10日目のまとめ

今日のキーポイントを短く整理するとこうなります。

メニュー付きコンソールアプリは、「メニュー表示 → 入力 → 分岐 → 処理 → ループ」の形が基本。
メニュー表示と番号入力は、それぞれ専用の関数に分けると読みやすい。
タスクは「辞書1件+リスト全体」で管理し、締切は datetime.date で持つ。
ファイル保存・読み込みは、7・9日目のパターンをタスク用に応用できる。
main 関数と if __name__ == "__main__": で、「ここがアプリの入口」と決められる。

もし余裕があれば、今日のタスク管理アプリに次の機能を足してみてください。

特定のタスクを削除する(番号を指定して消す)
締切が近いタスクだけを表示するメニュー項目を追加する
タスクに「メモ」や「優先度」を追加して、表示内容をリッチにする

ここから先は、もう「何を作るか」の世界です。
土台は十分できているので、あなたの生活や仕事で「これ自動化したいな」と思うものを、
少しずつこのスタイルでアプリにしていきましょう。

タイトルとURLをコピーしました