7日目のゴール
7日目のテーマは
「try / except を“完成形”としてまとめて、エラーに強い小さな実用アプリを作る」 ことです。
ここまで6日間で、あなたはすでに
入力エラーの防止
ビジネスルール(意味としておかしい値)のチェック
ファイル入出力のエラーハンドリング
リトライ回数の制御
ログ出力
どこで例外を処理するかの設計
を一通り通ってきました。
7日目は、それらを統合して
「実際に使えるレベルのエラーハンドリング付き入力アプリ」
「入力〜処理〜保存〜終了までの“流れ”を意識した設計」
を完成させます。
7日目の題材:タスク管理ミニアプリ(エラーに強い版)
アプリの仕様を言葉で整理する
作るのは、シンプルな「タスク管理アプリ」です。
タスク名(文字列、空は不可)
優先度(1〜5の整数)
期限日(任意。空なら「未設定」扱い)
これらを1件のタスクとして登録する
タスクはファイルに保存する(1行1タスク)
メニューで「追加」「一覧」「終了」が選べる
入力ミスやファイルエラーがあっても落ちない
この中に、7日間で学んだ要素を全部埋め込んでいきます。
エラー処理の「レイヤー」を先に決める
どの層が何を責任を持つかをはっきりさせる
このアプリを、次の4つの層に分けて考えます。
入力層
ユーザーから値を受け取る
型変換のエラー(ValueError)を処理する
リトライ回数を制御する
ビジネスルール層
「空文字はダメ」「1〜5の範囲」などの意味的なチェック
ルール違反を BusinessRuleError などで表現する
永続化層(保存・読み込み)
ファイルの読み書き
ファイルがない・書けないなどの OSError を処理する
ログを残す
アプリ全体層
メニューのループ
「最後の砦」として予期しない例外を受け止める
7日目のポイントは、
「try / except をこの4層にきれいに配置する」 ことです。
入力層:安全な入力関数を“完成版”にする
整数入力+リトライ制限付きの関数
まずは、優先度(1〜5)で使う整数入力から。
class InputRetryError(Exception):
pass
def input_int_with_limit(prompt, max_retries=3):
retries = 0
while retries < max_retries:
text = input(prompt)
try:
value = int(text)
except ValueError:
retries += 1
print(f"数字で入力してください。(残り {max_retries - retries} 回)\n")
continue
return value
raise InputRetryError("整数入力のリトライ回数上限に達しました。")
Pythonここでの重要ポイントは三つです。
「何回失敗したか」をカウントしていること
上限に達したら InputRetryError を投げて、呼び出し側に「失敗した」と伝えていることtry の中には「int に変換する1行だけ」を入れていること
これで、「型変換のエラー」と「リトライ上限」は
この関数の責任になります。
文字列入力(空禁止)の関数
タスク名は文字列ですが、「空はダメ」というルールを入れます。
class BusinessRuleError(Exception):
pass
def input_non_empty_text(prompt, max_retries=3):
retries = 0
while retries < max_retries:
text = input(prompt).strip()
try:
if text == "":
raise BusinessRuleError("空では入力できません。")
except BusinessRuleError as e:
retries += 1
print(str(e) + f"(残り {max_retries - retries} 回)\n")
continue
return text
raise InputRetryError("文字列入力のリトライ回数上限に達しました。")
Pythonここでのポイントは、
「意味としておかしい」(空文字)は BusinessRuleError
「何度も失敗した」は InputRetryError
と、エラーの種類を分けている ことです。
ビジネスルール層:タスクの意味をチェックする
優先度(1〜5)のチェック
優先度は 1〜5 の整数に限定します。
def input_priority():
while True:
try:
value = input_int_with_limit("優先度を入力してください(1〜5): ")
except InputRetryError as e:
print("優先度の入力に何度も失敗したため、登録を中止します。")
return None
if not (1 <= value <= 5):
print("優先度は1〜5の範囲で入力してください。\n")
continue
return value
Pythonここでの重要ポイントは二つです。
input_int_with_limit の責任は「整数として正しいか」「リトライ上限」まで
「1〜5の範囲かどうか」は、この関数側の責任
というふうに、責任範囲を分けていることです。
タスク名と期限日の入力をまとめる
期限日は、空なら「未設定」とします。
def input_task_info():
try:
title = input_non_empty_text("タスク名を入力してください: ")
except InputRetryError:
print("タスク名の入力に何度も失敗したため、登録を中止します。")
return None
priority = input_priority()
if priority is None:
return None
deadline = input("期限日を入力してください(未入力なら未設定): ").strip()
if deadline == "":
deadline = "未設定"
task = {
"title": title,
"priority": priority,
"deadline": deadline,
}
return task
Pythonここでのポイントは、
この関数の中には try / except が最小限しか出てこないこと
入力の細かいエラー処理は、下の関数に任せていること
です。
input_task_info は、
「タスクという1つの意味のある塊を作る」ことに集中しています。
永続化層:ファイル保存と読み込みのエラーハンドリング
ログ出力の関数
まずは、エラーを記録する小さなログ関数。
from datetime import datetime
def log_error(message):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
try:
with open("error.log", "a", encoding="utf-8") as f:
f.write(f"[{now}] {message}\n")
except OSError:
# ログすら書けない場合は、ここでは諦める(ユーザーには何も見せない)
pass
Pythonここでは、ログ書き込みのエラーは
「これ以上どうしようもない」と割り切って握りつぶしています。
タスクを1行で保存する関数
タスクは title,priority,deadline のCSV風で保存します。
FILENAME = "tasks.csv"
def append_task(task):
line = f"{task['title']},{task['priority']},{task['deadline']}\n"
try:
with open(FILENAME, "a", encoding="utf-8") as f:
f.write(line)
except OSError as e:
print("タスクをファイルに保存できませんでした。")
log_error(f"OSError in append_task: {e}")
return False
else:
return True
Pythonここでの重要ポイントは、
ファイルエラーはユーザーにメッセージを出しつつ
詳細はログに残し、戻り値で「失敗した」と伝えていること
です。
タスク一覧を読み込む関数
保存したタスクを読み込んで一覧表示に使います。
def load_tasks():
tasks = []
try:
f = open(FILENAME, "r", encoding="utf-8")
except FileNotFoundError:
# ファイルがない=まだタスクがない、という扱いにする
return []
except OSError as e:
print("タスクファイルを読み込めませんでした。")
log_error(f"OSError in load_tasks (open): {e}")
return []
else:
with f:
for line in f:
line = line.strip()
if line == "":
continue
parts = line.split(",")
if len(parts) != 3:
log_error(f"Invalid line in tasks.csv: {line}")
continue
title, priority_text, deadline = parts
try:
priority = int(priority_text)
except ValueError:
log_error(f"Invalid priority in tasks.csv: {priority_text}")
continue
tasks.append({
"title": title,
"priority": priority,
"deadline": deadline,
})
return tasks
Pythonここでの深掘りポイントは三つです。
ファイルがない場合は「エラー」ではなく「まだデータがない」と解釈していること
壊れた行(カンマ区切りで3要素でない)はスキップしてログに残していることtry の範囲を「本当にエラーが起きうる行」に絞っていること
これで、「一部の行が壊れていてもアプリ全体は動き続ける」状態になります。
アプリ全体層:メニューと「最後の砦」
タスク一覧表示
読み込んだタスクをきれいに表示します。
def show_tasks(tasks):
if not tasks:
print("登録されているタスクはありません。")
return
print("\n=== タスク一覧 ===")
for i, task in enumerate(tasks, start=1):
print(f"{i}. [優先度 {task['priority']}] {task['title']} (期限: {task['deadline']})")
Pythonここには try / except は出てきません。
「正しいデータが来る前提」で表示に集中します。
1件登録フロー
入力 → 作成 → 保存をひとまとまりにします。
def register_one_task():
task = input_task_info()
if task is None:
return
success = append_task(task)
if not success:
print("タスクの登録に失敗しました。")
return
print("タスクを登録しました。")
Pythonここでは、
入力層・永続化層のエラー処理を信頼して、
この関数自体は「流れ」に集中しています。
メインループと「最後の砦」
def input_menu_number():
while True:
text = input("番号を選んでください: ")
try:
value = int(text)
except ValueError:
print("数字で入力してください。\n")
continue
return value
def run_app():
while True:
print("\n=== タスク管理アプリ ===")
print("1: タスクを追加")
print("2: タスク一覧を見る")
print("0: 終了")
choice = input_menu_number()
if choice == 1:
register_one_task()
elif choice == 2:
tasks = load_tasks()
show_tasks(tasks)
elif choice == 0:
print("終了します。")
break
else:
print("その番号のメニューはありません。\n")
def main():
try:
run_app()
except Exception as e:
print("予期しないエラーが発生しました。アプリを終了します。")
log_error(f"Unexpected error in main: {e}")
main()
Pythonここでの重要ポイントは、
run_app の中では「想定内のエラー」はすべて下の層で処理されていることmain では「想定外のエラー」だけを最後に受け止めていること
です。
7日目で絶対に押さえてほしい本質
try / except は「アプリの骨格」の一部になる
7日間かけて、あなたは
単発の入力エラーを防ぐところから始まり
リトライ回数を制御し
ビジネスルールのエラーを分け
ファイルエラーを扱い
ログを残し
どの層がどのエラーを責任を持つかを設計し
最後に、
「エラーに強い小さなタスク管理アプリ」
という形にまとめました。
ここでの本質は、
try / except は「怖いからとりあえず書くもの」ではなく、
アプリの流れ・責任・ユーザー体験を
はっきりさせるための“設計の道具”だ、ということです。
この感覚を持てたあなたなら、
JSON保存アプリでも、Webアプリでも、GUIアプリでも、
「エラーとちゃんと付き合えるコード」を書いていけます。
ここから先は、
今日のタスク管理アプリを自分なりに拡張してみてください。
タスクの削除機能
優先度でのソート表示
期限が近いタスクだけの表示
どんな機能を足しても、
「どこで try / except を置くか」を考えながら書けば、
それはもう立派な中級者のコードです。


