5日目のゴール
5日目のテーマは
「try / except を“ユーザー体験”のために使う」 ことです。
ここまでであなたはすでに、
入力エラーを落とさず処理できる
安全な入力関数を作れる
ファイルやメニューにもエラーハンドリングを組み込める
どこで例外を処理するかを設計できる
というところまで来ています。
5日目では一歩進んで、
「何回も間違えたらどうするか」
「ユーザーにどこまで丁寧に伝えるか」
「ログ(記録)を残す」という発想
を加えて、“人が使うアプリとしてのエラーハンドリング” を育てていきます。
「何回でもやり直せる」は本当に正解かを考える
無限ループの優しさと、しんどさ
これまでの入力関数は、だいたいこんな形でした。
def input_int(prompt):
while True:
text = input(prompt)
try:
value = int(text)
except ValueError:
print("数字で入力してください。\n")
continue
return value
Pythonこれはこれで「落ちない」し、
何度でもやり直せるので優しいです。
でも、こういう状況を想像してみてください。
ユーザーがずっと「abc」と打ち続ける
実はキーボードが壊れていて数字が打てない
入力元が人間ではなく、バグったプログラム
このとき、while True は永遠に続きます。
「無限にやり直させる」ことが、
本当にユーザーにとって優しいのか?
ここに、5日目のテーマがあります。
「リトライ回数の上限」を設ける
一定回数失敗したら、いったん諦める
まずは、シンプルに「3回まで」にしてみます。
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
print("入力に失敗しました。処理を中断します。")
return None
Pythonここでの重要ポイントを深掘りします。
一つ目は、retries というカウンタを持っていること。
「何回失敗したか」をちゃんと数えています。
二つ目は、while retries < max_retries として、
ループの条件に「上限」を組み込んでいること。
三つ目は、上限に達したときにNone を返して「失敗した」という事実を呼び出し側に伝えていることです。
呼び出し側は、こう扱います。
age = input_int_with_limit("年齢を入力してください: ")
if age is None:
print("年齢が取得できなかったので、処理をスキップします。")
else:
print(f"あなたは {age} 歳ですね。")
Pythonここで大事なのは、
「入力に失敗した」という状態を、
ちゃんと“値”として表現している ことです。
「失敗したら例外を投げる」という設計もある
戻り値で表現するか、例外で表現するか
さっきは「失敗したら None を返す」方式でしたが、
もう一つの考え方として、
「一定回数失敗したら例外を投げる」
という設計もあります。
class InputRetryError(Exception):
pass
def input_int_with_limit_or_raise(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呼び出し側は、こうなります。
def main():
try:
age = input_int_with_limit_or_raise("年齢を入力してください: ")
except InputRetryError as e:
print(str(e))
print("年齢が取得できなかったため、アプリを終了します。")
return
print(f"あなたは {age} 歳ですね。")
main()
Pythonここでの本質は、
「入力に失敗した」という事実を
戻り値(None)で表現するか
例外として表現するか
を、意識して選ぶ ということです。
どちらが正解というより、
「このアプリでは、失敗したらどう扱いたいか」
「呼び出し側のコードが読みやすくなるのはどちらか」
を考えて決めるのが、中級者の感覚です。
エラーを「静かに」処理するか、「目立たせる」か
ユーザーにどこまで伝えるか
例えば、こんなコードを考えます。
def load_config(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
print("設定ファイルが見つかりませんでした。デフォルト設定を使います。")
return ""
Pythonこれは、「ファイルがない」というエラーを
ユーザーにメッセージで伝えつつ、
アプリとしては「デフォルトで続行する」という方針です。
一方で、こういうケースもあります。
def load_critical_data(filename):
with open(filename, "r", encoding="utf-8") as f:
return f.read()
Pythonここでのファイルは「絶対に必要なデータ」だとします。
この場合、
「ファイルがないけど、とりあえず空で続行する」
というのは、むしろ危険です。
このときは、あえて try / except を書かず、
「エラーが起きたらアプリ全体として止める」
という選択も正しいです。
5日目で意識してほしいのは、
エラーを静かに処理するのか
エラーをユーザーにしっかり知らせるのか
エラーが起きたらアプリを止めるのか
を、ケースごとに選び分ける ということです。
「ログを残す」という発想を足す
目の前のユーザーだけでなく、「あとから振り返る自分」のために
エラーが起きたとき、
ユーザーには簡単なメッセージだけを見せて、
詳しい情報は「ログ」に残す、というやり方があります。
まずは、超シンプルなログ関数を作ります。
from datetime import datetime
def log_error(message):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open("error.log", "a", encoding="utf-8") as f:
f.write(f"[{now}] {message}\n")
Pythonこれを try / except の中で使います。
def load_text_file(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError as e:
print("ファイルが見つかりませんでした。")
log_error(f"FileNotFoundError: {e}")
return ""
except OSError as e:
print("ファイルを読み込めませんでした。")
log_error(f"OSError: {e}")
return ""
Pythonここでの重要ポイントは二つです。
一つ目は、ユーザーにはシンプルなメッセージだけを見せていること。
「専門用語だらけのエラー」をそのまま見せると、
かえって不安にさせてしまいます。
二つ目は、log_error で詳細を残していること。
あとから「何が起きていたのか」を自分で調べられます。
これが、「人が使うアプリ」としての
“優しさと責任” のバランス です。
5日目のミニアプリ:リトライ制限+ログ付き入力アプリ
仕様を決める
簡単な「会員登録風」アプリを作ります。
年齢(整数、1〜120)
年齢入力は最大3回まで
3回失敗したら登録を中止
失敗や中止の情報は error.log に記録
これに、今日の try / except の考え方を組み込みます。
コード全体像
from datetime import datetime
class InputRetryError(Exception):
pass
def log_error(message):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open("error.log", "a", encoding="utf-8") as f:
f.write(f"[{now}] {message}\n")
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")
log_error(f"ValueError in input_int_with_limit: text='{text}'")
continue
return value
raise InputRetryError("整数入力のリトライ回数上限に達しました。")
def input_age():
while True:
try:
age = input_int_with_limit("年齢を入力してください(1〜120): ")
except InputRetryError as e:
print("年齢の入力に何度も失敗したため、登録を中止します。")
log_error(f"InputRetryError in input_age: {e}")
return None
if not (1 <= age <= 120):
print("1〜120の範囲で入力してください。\n")
log_error(f"BusinessRuleError in input_age: age={age}")
continue
return age
def run_app():
print("=== 会員登録アプリ ===")
age = input_age()
if age is None:
print("登録は完了しませんでした。")
return
print(f"\n登録が完了しました。年齢: {age} 歳")
def main():
try:
run_app()
except Exception as e:
print("予期しないエラーが発生しました。アプリを終了します。")
log_error(f"Unexpected error in main: {e}")
main()
Pythonここに、5日目のエッセンスが全部入っています。
input_int_with_limit で「リトライ上限」を設けている
上限に達したら InputRetryError を投げて、呼び出し側で方針を決めている
ユーザーにはシンプルなメッセージ、詳細は error.log に残しているmain で「最後の砦」として予期しないエラーをログに残している
5日目で絶対に押さえてほしい本質
try / except は「人の体験」をデザインする道具になる
今日いちばん伝えたいのは、
try / except は
単に「落ちないようにする」ためだけではなく、
何回までやり直させるか
失敗したときにどう終わらせるか
ユーザーには何を見せて、何をログに残すか
といった、ユーザー体験そのものをデザインするための道具
になっていく、ということです。
ここまで来たあなたはもう、
エラーを恐れて避ける側ではなく、
エラーが起きる前提で「どう付き合うか」を設計する側
に立っています。
6日目・7日目では、
このエラーハンドリングを他のアプリ(JSON保存アプリなど)と
組み合わせて、「現実のアプリ」に近づけていきましょう。


