
Table of Contents(目次)
Pythonの例外処理をFortune Cookieプロジェクトに実装する方法
このレクチャーでは、実際のプロジェクト(Fortune Cookie)に例外処理を組み込む方法を学びます。ポイントは、例外を細かく多く書くのではなく、起こり得る問題に対して安全に処理できる範囲で、1〜2か所に簡潔な例外処理を置くことです。
- 実際のプログラム(
fortune.py及びlanguage_manager.py)に try-except を導入して、想定される失敗を安全に扱えるようにする。 - エラーが起きたときにユーザーにわかりやすく伝える簡潔なメッセージを表示する。
- 例外処理は「全部を捕まえる」のではなく「意味のある箇所で限定的に適用する」ようにする。
Pythonでよく発生するエラーの種類と原因のチェックポイント
モジュールやパッケージの作成・実行時に発生する一般的なエラーは、次の3種類です。
- ImportError
- import 文でモジュールをロードしようとして問題が発生すると送出されます。
- ModuleNotFoundError
ImportErrorのサブクラスで、import 文でモジュールが見つからない場合に送出されます。
- AttributeError
- 存在しない属性(変数や関数など)を参照しようとした場合に送出されます。対象モジュールに変数や関数が正しく定義されているか確認してください。
Pythonコードのどこに例外処理を書くべきか(重要ポイント)
実務的に安全なプログラムにするためには、次の2点だけ押さえれば十分です。今回はこれら2か所に例外処理を入れます。
- モジュールの import 部分
- モジュールファイル(例:
message/language_jp.py)が存在しない、または読み込めない場合に備える。
- モジュールファイル(例:
- データ取得/実行部(関数呼び出しなど)
- モジュールは読み込めても、中身のデータが想定外の形式でアクセスに失敗する場合に備える。
例外処理をあちこちに散りばめると、かえって何が起きたのか追跡しづらくなります。ですので「重要な境界(モジュール読み込み、外部入出力、ユーザー入力のパース)」に絞るのがベストプラクティスです。
Python例外処理を追加する前のコード例(Before)
まずは改修前の状態を振り返ります。以下は、これまでの流れで使ってきたfortune.pyとmanager/language_manager.pyです。
(if __name__ == '__main__':行より下は省略)
# fortune.py
import random
import manager.language_manager as lang_manager
def main():
# 言語を選択
lang_code = input('言語を選んでください (jp/en): ')
# 言語コードに対応したモジュールが返される
lang_module = lang_manager.get_language_module(lang_code)
messages = lang_module.MESSAGES
fortunes = lang_module.FORTUNES
# クッキーを開きますか?
print(f"=== {messages['app_title']} ===")
answer = input(messages['prompt_open'])
if answer == 'y':
print(f"{messages['fortune_prefix']} {random.choice(fortunes)}")
else:
print(messages['goodbye'])
# manager/language_manager.py
def get_language_module(lang_code):
'''言語コードに応じてモジュールを返す'''
if lang_code == 'en':
from message import language_en
return language_en
elif lang_code == 'jp':
from message import language_jp
return language_jp
else:
print('対応していない言語コードです。日本語を使用します。')
from message import language_jp
return language_jp
Python例外処理を実装したコード例(After)
importエラー(ImportError / ModuleNotFoundError)の安全な処理方法
- モジュールの
import部分はtry-exceptで保護し、失敗したら「エラー表示→プログラム終了」する。
# manager/language_manager.py
import sys
def get_language_module(lang_code):
'''言語コードに応じてモジュールを返す'''
try:
if lang_code == 'en':
from message import language_en
return language_en
elif lang_code == 'jp':
from message import language_jp
return language_jp
else:
print('対応していない言語コードです。日本語を使用します。')
from message import language_jp
return language_jp
except ImportError as e:
print('ImportError: 必要なメッセージモジュールが見つかりません。')
print('詳しい情報:', e)
print('プログラムを終了します')
sys.exit(1) # 非ゼロで終了(異常終了を示す)
コードのポイント:
- 【2行目・21行目】
import sys,sys.exit(1)sysモジュールは終了時にステータスコードを返すために使います。sys.exit(1)は「エラー終了」を示します。- 補足説明:
sys.exit()はPython を終了させ、SystemExit例外を送出します。引数に整数を渡した場合、シェル等は 0 は "正常終了"、0 以外の整数を "異常終了" として扱います。どちらの場合も、Pythonの処理を終了させ、「SystemExit例外を送出する」ことは共通です。
- 【6行目~16行目】
try: ~- モジュールが見つからない時などに、
ImportError/ModuleNotFoundErrorが発生するため、ここをtryで囲みます。
- モジュールが見つからない時などに、
- 【17行目】
except ImportError as e:ModuleNotFoundErrorはImportErrorのサブクラスですので、ここではImportErrorでエラーを捕捉しています。ModuleNotFoundErrorが発生した場合も、ImportErrorで捕捉することができます。
- 【18行目以降】
- ユーザー(または運用者)に何が起きたかがわかるよう、エラーの内容と技術的な詳細(
e)を出力して安全に終了します。
- ユーザー(または運用者)に何が起きたかがわかるよう、エラーの内容と技術的な詳細(
実行時エラーをmainでまとめて処理する方法
モジュールの情報を受け取った後の処理(各言語のメッセージ取得など)は、汎用的なexceptで補足して「詳細を出力→安全に終了」する。
# fortune.py
import random
import manager.language_manager as lang_manager
def main():
try:
# 言語を選択
lang_code = input('言語を選んでください (jp/en): ')
# 言語コードに対応したモジュールが返される
lang_module = lang_manager.get_language_module(lang_code)
messages = lang_module.MESSAGES
fortunes = lang_module.FORTUNES
# クッキーを開きますか?
print(f"=== {messages['app_title']} ===")
answer = input(messages['prompt_open'])
if answer == 'y':
print(f"{messages['fortune_prefix']} {random.choice(fortunes)}")
else:
print(messages['goodbye'])
except KeyError as e:
# もしMESSAGESのdictに期待するキーが欠けていた場合の対処
print('内部エラー: 設定されている文言に不備があります。')
print('不足しているキー:', e)
except Exception as e:
# 想定外の例外はここで捕まえて情報を出しておく
print('予期せぬエラーが発生しました。')
print('詳細:', e)
コードのポイント:
- 【8行目】
def main():の中のtry- 実際の処理全体を
tryで囲んでおくと、想定外のエラーが起きても、プログラムが即終了するのを防げます。
- 実際の処理全体を
- 【24行目】
except KeyError as e:- 言語データの
MESSAGESdictに、必要なキーが欠けている場合に備えて個別に捕捉し、エラー内容を出力します。
- 言語データの
- 【28行目】
except Exception as e:- 最終的な保険です。ここでエラーメッセージを見て、必要ならログやバグ報告につなげます。
Pythonの例外処理におけるユーザー通知とログ記録の基本
初心者のうちはprint()で事足りますが、実務ではloggingモジュールを使って、エラーをファイルや外部の監視システムに記録します。ここでは2つの原則だけ押さえておきましょう。
- ユーザー向けメッセージ(print)は簡潔に:ユーザーには「何が起きたか」「次に何をすればよいか」を短く提示します(例:「エラー: 必要なモジュールが見つかりません。管理者に連絡してください。」)。
- 技術情報(例外の詳細)はログへ:スタックトレースや例外オブジェクトはログに残すのが基本です。参考: Python: logging
Python例外処理が正しく動くか確認するテスト項目
例外処理のコードを記述したら、一時的にエラーが出るようなコードに書き換えてみて、想定通りにエラーが補足されるか確認してみましょう。
- テスト1:通常動作確認:全ファイルが揃った状態で実行し、言語メッセージが正しく表示されるか確認する。(何のエラーも出ないことを確認する。)
- テスト2:モジュール欠落時の挙動:一時的に
message/language_jp.pyのファイル名を変えて実行し、エラーメッセージが表示されて終了するか確認する。 - テスト3:メッセージキー欠落の挙動:languageモジュール内で意図的に
MESSAGESのキーを消して実行し、KeyErrorがハンドルされるか確認する。
Python例外処理のポイントまとめ(実務で役立つ基礎)
このレクチャーでは、「プログラムが起きうる失敗に対して、ユーザーと運用者の両方にとって分かりやすく・安全に振る舞う」ための最低限の仕組みを整えました。
- 例外処理は「多すぎても少なすぎてもよくない」ので、重要箇所(import、外部IO、ユーザー入力)に絞るのが良い。
- 今回は import 部分と実行部の2か所にシンプルな例外処理を追加した。
- 将来的には
loggingを導入して、例外の詳細をファイルに残す運用へ進めると良いでしょう。






