例外を処理します。

完了

出力として大量のトレースバックを示す例外を最初に見つけたときに、すべてのエラーをキャッチして、それが起こらないようにしようと思うかもしれません。

もしあなたが火星探査のミッション中に、ナビゲーション システムのテキストに "エラーが発生しました" というメッセージが表示された場合、どうすればよいでしょうか。 その他の情報やコンテキストは存在せず、点滅する赤いライトとこのエラー テキストしかないことを想像してみてください。 開発者は、プログラムの反対側に立ってみることが役に立ちます。つまり、エラーが発生した場合にユーザーは何ができるのでしょうか。

このモジュールでは、例外をキャッチすることによって例外を処理する方法を説明していますが、常に例外をキャッチする必要はありません。 他の呼び出し元がエラーを処理できるように、例外を発生させると便利な場合があります。

try と except のブロック

ここでは、ナビゲーターの例を使用して、火星ミッションの構成ファイルを開くコードを作成します。 構成ファイルはあらゆる種類の問題を引き起こす可能性があるため、問題が発生したときに正確に報告することが重要です。 ファイルまたはディレクトリが存在しない場合、FileNotFoundError が発生することがわかっています。 この例外を処理する場合は、tryexcept のブロックを使用できます。

try:
     open('config.txt')
except FileNotFoundError:
     print("Couldn't find the config.txt file!")

Couldn't find the config.txt file!

try キーワードの後に、例外を発生させる可能性のあるコードを追加します。 次に、except キーワードと考えられる例外を追加し、その後に、その条件が発生したときに実行する必要があるコードを続けます。 config.txt がシステムに存在しないため、Python は構成ファイルが存在しないことを出力します。 tryexcept のブロックと役に立つメッセージにより、トレースバックを防止しながら、問題についてユーザーに通知します。

ファイルが存在しないことは一般的ですが、検出される唯一のエラーではありません。 ファイルが存在する場合でも、ファイルのアクセス許可が無効なためにファイルを読み取れない場合があります。 Visual Studio Code で config.py という名前の Python ファイルを新規作成しましょう。 ナビゲーション システムの構成ファイルを見つけて読み取る次のコードをファイルに追加します。

def main():
    try:
        configuration = open('config.txt')
    except FileNotFoundError:
        print("Couldn't find the config.txt file!")


if __name__ == '__main__':
    main()

次に、config.txt という名前の "ディレクトリ" を作成します。 config.py ファイルを呼び出して、次のような新しいエラーを確認します。

python3 config.py
Traceback (most recent call last):
  File "/tmp/config.py", line 9, in <module>
    main()
  File "/tmp/config.py", line 3, in main
    configuration = open('config.txt')
IsADirectoryError: [Errno 21] Is a directory: 'config.txt'

このエラーを処理する方法として、発生する可能性のあるすべての例外をキャッチして、トレースバックを防ぐことは役に立ちません。 すべての例外をキャッチすることがなぜ問題なのかを理解するために、新しく作成した config.py ファイルで main() 関数を更新して試してみてください。

def main():
    try:
        configuration = open('config.txt')
    except Exception:
        print("Couldn't find the config.txt file!")

次に、アクセス許可が不適切な config.txt ディレクトリが存在する場所でコードをもう一度実行します。

python3 config.py
Couldn't find the config.txt file!

ここでの問題は、エラー メッセージが正しくないことです。 ディレクトリは存在しますが、アクセス許可が異なり、Python で読み取ることができません。 ソフトウェア エラーを処理する際、次のようなエラーに悩まされる場合があります。

  • 本当の問題が何かを示していない。
  • 実際の問題と一致しない出力を出す。
  • 問題を解決するために何が可能かを示さない。

これらのすべての不満に対処するために、コードを修正しましょう。 FileNotFoundError をキャッチするように戻し、さらに PermissionError をキャッチする別の except ブロックを追加します。

def main():
    try:
        configuration = open('config.txt')
    except FileNotFoundError:
        print("Couldn't find the config.txt file!")
    except IsADirectoryError:
        print("Found config.txt but it is a directory, couldn't read it")

それでは、config.txt ディレクトリがある同じ場所でもう一度実行します。

python3 config.py
Found config.txt but couldn't read it

次に config.txt ディレクトリを削除し、代わりに最初の except ブロックに到達することを確認します。

 rm -f config.txt
 python3 config.py
Couldn't find the config.txt file!

エラーが同様の性質を持ち、個別に処理する必要がない場合は、except 行でかっこを使用して例外を 1 つにまとめることができます。 たとえば、ナビゲーション システムの負荷が高く、ファイル システムがビジー状態になった場合は、BlockingIOErrorTimeOutError を一緒にキャッチすることは理にかなっています。

def main():
    try:
        configuration = open('config.txt')
    except FileNotFoundError:
        print("Couldn't find the config.txt file!")
    except IsADirectoryError:
        print("Found config.txt but it is a directory, couldn't read it")
    except (BlockingIOError, TimeoutError):
        print("Filesystem under heavy load, can't complete reading configuration file")

ヒント

例外をグループ化することはできますが、個別に処理する必要がない場合にのみ行ってください。 大量の例外をグループ化して、汎用的なエラー メッセージを出すことは避けてください。

例外に関連付けられているエラーにアクセスする必要がある場合は、except 行を更新して as キーワードを含める必要があります。 この手法は、例外が汎用的すぎて、エラー メッセージが役に立つ場合に便利です。

try:
    open("mars.jpg")
except FileNotFoundError as err:
     print("Got a problem trying to read the file:", err)
Got a problem trying to read the file: [Errno 2] No such file or directory: 'mars.jpg'

この場合、as err は、err が例外オブジェクトを値として持つ変数になることを意味します。 次に、この値を使用して、例外に関連付けられているエラー メッセージを出力します。 この手法を使用するもう 1 つの理由は、エラーの属性に直接アクセスする場合です。 たとえば、FilenotFoundErrorPermissionError の両方の "親例外" である、より汎用的な OSError 例外をキャッチする場合は、.errno 属性でそれらを区別できます。

try:
    open("config.txt")
except OSError as err:
     if err.errno == 2:
         print("Couldn't find the config.txt file!")
     elif err.errno == 13:
        print("Found config.txt but couldn't read it")
Couldn't find the config.txt file!

常に、コードの読みやすさが最適になり、将来のメンテナンスに役立つ手法を使用してください。 エラーが発生した場合に、より優れたユーザー エクスペリエンスを提供するために、理解しにくいコードを使用する必要がある場合があります。