このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。

【Python入門】try文とfinally節

try文とfinally節

 try文とfinally節を用いると、例外の有無にかかわらず必ず実行したい後片付け処理を記述できます。さらに、try文はネスト(入れ子)することができ、内側のtry節で例外が発生しても、内側のfinally節は必ず実行され、その後外側の例外処理に制御が移る仕組みになっています。
 ここでは、try文とfinally節の基本的な使い方に加え、ネストしたtry文におけるfinally節の動作例を、具体的なプログラム例とともに解説します。

プログラムのダウンロード

 「ダウンロード」から、JupyterLab で実行できるサンプルプログラムがダウンロードできます。ファイルは、ESET Endpoint Securityでウイルスチェックをしておりますが、ダウンロードとプログラムの実行は自己責任でお願いいたします。

1.try文とfinally節の基本

1.1. try節の役割

 try節は、エラーが発生する可能性のある処理をまとめて記述する部分です。例外が発生しなければ、try節内のすべての文が順次実行されます。

1.2. finally節の役割

 finally節は、例外の有無にかかわらず必ず実行される処理を記述します。たとえば、リソースの解放や仕切り線の表示、ログ記録など、後片付けに必要な処理を行う際に利用されます。

以下の表は、try節とfinally節の役割をまとめたものです。

役割実行タイミング
try節エラー発生の可能性がある処理を記述正常時は全処理、エラー発生時は直ちに中断
finally節エラー発生の有無にかかわらず必ず実行する処理try節またはexcept節の後、必ず実行

2.try文とfinally節を使ったプログラム例

2.1. 基本例:平方根計算プログラム

 ここでは、ユーザーに非負の数値を入力させ、その平方根を計算して表示するプログラムを示します。入力が不正な場合にはValueErrorが発生しますが、finally節を用いて常に仕切り線を表示し、次の入力に備えます。

import math

while True:
    try:
        # ユーザーから非負の数値を入力して実数に変換する
        num = float(input("Enter a non-negative number: "))
        if num < 0:
            # 負の値の場合は明示的に例外を発生させる
            raise ValueError("Negative value is not allowed.")
        # 平方根を計算して表示
        print("Square root:", math.sqrt(num))
    except ValueError as e:
        # 不正な入力の場合はエラーメッセージを表示
        print("Error:", e)
    finally:
        # 例外の有無にかかわらず、仕切り線を表示
        print("=" * 25)

実行結果

Enter a non-negative number:  10
Square root: 3.1622776601683795
=========================
Enter a non-negative number:  -10
Error: Negative value is not allowed.
=========================

解説

  • try節内でユーザーからの入力をfloatに変換し、負の値が入力された場合にはValueErrorを発生させています。
  • except節では、発生したValueErrorを捕捉し、例外オブジェクト(e)を使ってエラーメッセージを表示します。
  • finally節は、例外が発生したかどうかにかかわらず、25個の等号を表示して次の入力への区切りとしています。

2.2. ネストしたtry文におけるfinally節の動作例

 次に、try文が入れ子になっている場合に、内側と外側のfinally節がどのように動作するかを示すプログラム例です。ここでは、外側のtry文内に内側のtry文を定義し、内側でZeroDivisionErrorが発生する状況をシミュレートします。

try:  # 外側のtry文
    print("Level 1: Start")
    try:  # 内側のtry文
        print("Level 2: Start")
        # ここでZeroDivisionErrorを発生させる(0で割る)
        result = 10 / 0
        print("Level 2: End")  # この行は実行されない
    except ValueError:
        # 内側のtry文ではValueErrorを処理するが、今回は該当しない
        print("Level 2: Caught ValueError")
    finally:
        # 内側のfinally節は必ず実行される
        print("Level 2: Finally block executed")
    print("Level 1: Continuing after inner try")
except ZeroDivisionError:
    # 外側のexcept節で、内側で発生したZeroDivisionErrorを捕捉
    print("Level 1: Caught ZeroDivisionError")
finally:
    # 外側のfinally節は全体の後片付けとして実行される
    print("Level 1: Finally block executed")
print("Program continues...")

実行結果

Level 1: Start
Level 2: Start
Level 2: Finally block executed
Level 1: Caught ZeroDivisionError
Level 1: Finally block executed
Program continues...

解説

  • 外側のtry節で「Level 1: Start」が出力され、次に内側のtry節で「Level 2: Start」が出力されます。
  • 内側のtry節で10 / 0が実行され、ZeroDivisionErrorが発生するため、内側のtry節以降の処理はスキップされ、内側のexcept節はValueErrorの処理を試みるが該当せず、finally節に移ります。
  • 内側のfinally節で「Level 2: Finally block executed」が出力され、例外が外側に伝播され、外側のexcept節でZeroDivisionErrorが捕捉され「Level 1: Caught ZeroDivisionError」が出力されます。
  • 最後に外側のfinally節で「Level 1: Finally block executed」が実行され、try文の外側の処理「Program continues...」が実行されます。

まとめ

 try文とfinally節を組み合わせることで、例外が発生した場合でも必ず実行すべき後片付け処理を記述でき、プログラムの実行フローを整然と保つことが可能になります。

  • 基本例では、平方根計算プログラムにおいて、例外の有無にかかわらず仕切り線を表示する処理をfinally節に記述しました。
  • ネストしたtry文の例では、内側と外側のfinally節がどのように連携して実行されるかを示し、エラー発生時の制御フローが明確になります。

 これらの技法を活用することで、エラー発生時にも安全で読みやすいプログラムを書くことができるようになります。