Catch Me If You Can: Python での例外処理ガイド

May 09 2023
スマートな例外管理によって Python の可能性を最大限に引き出す ソフトウェア開発者として、例外の処理は必要悪と見なされることがよくあります。それでも、Python の例外処理システムを習得すれば、より効率的で効果的なプログラマーになることができます。

スマートな例外管理を通じて Python の可能性を最大限に引き出す

UnsplashのCookie the Pomによる写真

ソフトウェア開発者として、例外の処理は必要悪と見なされることがよくあります。それでも、Python の例外処理システムを習得すれば、より効率的で効果的なプログラマーになることができます。

このブログ投稿では、次の内容について詳しく説明します。

  • 例外処理とは
  • ifステートメントと例外処理の違い
  • 適切なエラー管理のためのelseand句の使用finally
  • カスタム例外の定義
  • 例外処理のベスト プラクティス

例外処理は、プログラムの実行中に発生する可能性のあるエラーまたは例外をキャッチして処理するコードを記述するプロセスです。これにより、開発者は、完全にクラッシュするのではなく、予期しないイベントやエラーが発生した場合でも実行し続ける堅牢なコードを作成できます。

例外が発生すると、Python は一致する例外ハンドラーを検索します。ハンドラー コードが実行され、エラーのログ記録、エラー メッセージの表示、エラーからの回復の試行などの適切なアクションが実行されます。全体として、例外処理は、Python アプリケーションの信頼性と保守性を高め、デバッグを容易にするのに役立ちます。

ifステートメントと例外処理の違い

Python でのステートメントと例外処理の主な違いは、ifそれぞれの目標と使用シナリオにあります。

このifステートメントは、構造化プログラミングの基本的なビルディング ブロックとして機能します。条件を評価し、条件が true か false かに基づいてさまざまなコード ブロックを実行します。次に例を示します。

temperature = int(input("Please enter temperature in Fahrenheit: "))
if temperature > 100:
    print("Hot weather alert! Temperature exceeded 100°F.")
elif temperature >= 70:
    print("Warm day ahead, enjoy sunny skies.")
else:
    print("Bundle up for chilly temperatures.")

例外は、問題を通知し、改善、デバッグ、または追加のエラー チェック手段が必要なコード内の領域を示すために使用されます。これらにより、Python は誤った状況を適切に処理し、スクリプトを突然終了するのではなく実行し続けることができます。

ゼロ除算に関連する潜在的なエラーをより適切に管理するために例外処理を実装する方法について、次の例を検討してください。

# Define a function that tries to divide a number by zero
def divide(x, y):
    result = x / y
    return result
# Call the divide function with x=5 and y=0
result = divide(5, 0)
print(f"Result of dividing {x} by {y}: {result}")

Traceback (most recent call last):
  File "<stdin>", line 8, in <module>
ZeroDivisionError: division by zero attempted

try-except上記の例外は、「divide」関数の呼び出しを次のようにブロック内にラップすることで処理できます。

# Define a function that tries to divide a number by zero
def divide(x, y):
    result = x / y
    return result
# Call the divide function with x=5 and y=0
try:
    result = divide(5, 0)
    print(f"Result of dividing {x} by {y}: {result}")
except ZeroDivisionError:
    print("Cannot divide by zero.")

Cannot divide by zero.

Python の組み込み例外の詳細については、[2]を参照してください。

適切なエラー管理のための Else 句と finally 句の使用

Python で例外を処理する場合は、ブロックに節elseと節の両方を含めることをお勧めします。この句を使用すると、例外が発生しない場合に何が起こるかを指定できますが、この句により、例外が発生したかどうかに関係なく、特定のクリーンアップ操作が常に実行されることが保証されます[1] [2]。finallytry-exceptelsefinally

たとえば、ファイルからデータを読み取り、そのデータに対して何らかの操作を実行するシナリオを考えてみます。ファイルの読み取り中に例外が発生した場合、エラーをログに記録して処理を停止したい場合がありますが、それでもファイルを適切に閉じたい場合があります。

elseand句を使用すると、finally例外が発生しない場合は通常どおりデータを処理するか、最後にファイルを閉じながら例外を適切に処理することができます。これらの句がないと、コードはリソース リークや不完全なエラー処理に対して脆弱になります。したがって、それらは堅牢で信頼性の高いプログラムを作成する上で重要な役割を果たします。

try:
    # Open the file in read mode
    file = open("file.txt", "r")
    print("Successful opened the file")
except FileNotFoundError:
    # Handle missing files
    print("File Not Found Error: No such file or directory")
    exit()
except PermissionError:
    # Handle permission issues
    print("Permission Denied Error: Access is denied")
else:
    # All good, do something with the file data
    content = file.read().decode('utf-8')
    processed_data = process_content(content)
    
# Cleanup after ourselves even if an exception occurred above
finally:
    file.close()

それ以外の場合、ブロック内で例外が発生しない場合はtry、ブランチ内のファイル コンテンツの処理を続行しますelse。最後に、ブロックによって保証されるクリーンアップ操作により、finally以前に例外が発生したかどうかに関係なく、ファイルが閉じられます[1]。

このような構造化されたアプローチを採用することで、外部システムや入力とのやり取りから発生する可能性のある潜在的なエラーを考慮しながら、コードを整理して簡単に追跡できます。

カスタム例外の定義

ExceptionPython では、や から直接継承するその他のクラスなどの組み込み例外をサブクラス化することにより、カスタム例外を定義できますException

これを行うには、これらの基本例外の 1 つから継承する新しいクラスを作成し、ニーズに固有の属性を追加する必要があります。その後、他の組み込み例外クラスを使用する場合と同様に、新しく定義した例外クラスをコード全体で使用できます。

と呼ばれるカスタム例外を定義する例を次に示しますInvalidEmailAddress

class InvalidEmailAddress(ValueError):
    def __init__(self, message):
        super().__init__(message)
        self.msgfmt = message

無効な電子メール アドレス形式に遭遇したときはいつでも、この例外を発生させることができます。

def send_email(address):
    if isinstance(address, str) == False:
        raise InvalidEmailAddress("Invalid email address")
# Send email

>>> send_email(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/project/main.py", line 8, in send_email
    raise InvalidEmailAddress("Invalid email address")
InvalidEmailAddress: Invalid email address

Python でのエラー処理に関連するベスト プラクティスを次に示します。

  1. 失敗に備えた設計: 起こりうる失敗を考慮し、それらを適切に処理するようにプログラムを設計することで、前もって計画を立てます。これは、エッジ ケースを予測し、適切なエラー ハンドラーを実装することを意味します。
  2. 説明的なエラー メッセージを使用する: 詳細なエラー メッセージまたはログを提供して、ユーザーが問題の原因と理由を理解できるようにします。「エラーが発生しました」や「問題が発生しました」などの一般的なエラー メッセージは避けてください。代わりに、解決策を提案したり、ドキュメントへのリンクを提供したりするわかりやすいメッセージを表示します。詳細な指示を提供することと、不要なコンテンツで UI を乱雑にしないこととの間でバランスを取るようにしてください。
  3. 副作用を最小限に抑える: try-finally または try-with-resources ブロックを使用して問題のあるコード セクションを分離することにより、失敗したアクションの結果を最小限に抑えます。成功または失敗の結果に関係なく、クリーンアップ タスクが常に実行されるようにします。
  4. 徹底的にテストする: 徹底的にテストする: 包括的なテストを実行して、さまざまなシナリオで例外ハンドラーが正しく動作することを確認します。
  5. 定期的なリファクタリング: エラーが発生しやすいコード セグメントをリファクタリングして、信頼性とパフォーマンスを向上させます。コードベースをモジュール化して疎結合に保ち、独立した部分が他の部分に悪影響を与えることなく独立して進化できるようにします。
  6. 重要なイベントをログに記録する:ファイルまたはコンソール出力にログを記録して、アプリケーションで発生した興味深いイベントを追跡します。これにより、大量の非構造化ログをふるいにかけることなく、問題を迅速に診断できます。

エラー処理コードを記述することは、開発者がより信頼性の高い堅牢なアプリケーションを構築できるようになるため、特に Python を使用する場合、ソフトウェア開発の不可欠な部分です。業界標準とベスト プラクティスに従うことで、開発者はデバッグ時間を短縮し、コードの品質を確保して、より優れたユーザー エクスペリエンスを提供できます。

資力

[1]https://docs.python.org/3/tutorial/errors.html

[2]https://www.geeksforgeeks.org/python-exception-handling/