web-dev-qa-db-ja.com

Pythonで「finally」節が必要なのはなぜですか?

try...except...finallyステートメントでfinallyが必要な理由がわかりません。私の意見では、このコードブロック

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

finallyを使用するこれと同じです:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

何か不足していますか?

232
RNA

早めに戻ると違いがあります:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

これと比較してください:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

違いを引き起こす可能性のある他の状況:

  • Exceptブロック内で例外がスローされた場合。
  • run_code1()で例外がスローされたが、TypeErrorではない場合。
  • continueおよびbreakステートメントなどの他の制御フローステートメント。
324
Mark Byers

finallyを使用すると、例外をキャッチしなくても、例外が発生したかどうかに関係なく、ファイルまたはリソースが閉じられたり解放されたりすることができます(または、specific例外をキャッチしない場合)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

この例では、withステートメントを使用した方が良いでしょうが、この種の構造は他の種類のリソースに使用できます。

数年後、読者が面白いと感じるかもしれないfinallyの悪用について ブログ投稿 を書きました。

67
kindall

それらは同等ではありません。最後に、他に何があってもコードが実行されます。実行する必要があるクリーンアップコードに役立ちます。

21
Antimony

上記の他の回答に追加するために、finally句は例外が発生しなかった場合にのみ実行されるのに対して、else句は実行されます。

たとえば、例外なしでファイルに書き込むと、以下が出力されます:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

出力:

Writing to file.
Write successful.
File closed.

例外がある場合、コードは次を出力します(意図的なエラーはファイルを読み取り専用にしておくことに注意してください。

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

出力:

Could not write to file.
File closed.

finally句が例外に関係なく実行されることがわかります。お役に立てれば。

11
captainblack

コードブロックは同等ではありません。 run_code1()finally以外の例外をスローした場合、またはrun_code2()が例外をスローした場合、TypeError句も実行されますが、これらの場合、最初のバージョンのother_code()は実行されません。

7
Sven Marnach

最初の例では、run_code1()TypeErrorではない例外を発生させた場合はどうなりますか? ... other_code()は実行されません。

それをfinally:バージョンと比較してください:other_code()は、発生する例外に関係なく実行されることが保証されています。

7
mhawke

finally"clean up actions"を定義するためのものです。 finally句は、例外を処理しなくても例外が発生したかどうかに関係なく、tryステートメントを終了する前にイベントで実行されます。

2番目の@Byersの例。

3
kakhkAtion

最後に、メイン作業用のコードを実行する前に「オプション」コードを実行したい場合にも使用できます。そのオプションコードはさまざまな理由で失敗する可能性があります。

次の例では、store_some_debug_infoがどのような例外をスローするか正確にはわかりません。

実行できます:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

しかし、ほとんどのリンターは、あまりにもあいまいな例外をキャッチすることに文句を言うでしょう。また、エラーに対してpassだけを選択しているため、exceptブロックは実際には値を追加しません。

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

上記のコードは、コードの最初のブロックと同じ効果がありますが、より簡潔です。

2
Brad Johnson

完璧な例は次のとおりです。

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)
2
Abhijit Sahu

デルファイを数年間専門的に使用することで、最終的に使用するクリーンアップルーチンを保護することができました。 Delphiでは、メモリリークが発生しないように、tryブロックの前に作成されたリソースをクリーンアップするために、finallyの使用をほぼ強制しています。これは、Java、Python、およびRubyの動作方法でもあります。

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

リソースは、試行と最終の間に何をするかに関係なくクリーンアップされます。また、実行がtryブロックに到達しない場合、クリーンアップされません。 (つまり、create_resource自体が例外をスローします)コードを「例外安全」にします。

最終ブロックが実際に必要な理由については、すべての言語が必要なわけではありません。 C++では、例外がスタックを展開したときにクリーンアップを強制するデストラクタを自動的に呼び出しました。私はこれがtry ... finally言語と比較してよりクリーンなコードの方向へのステップアップだと思います。

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.
2
nurettin

documentation で説明したように、finally句は、すべての状況下で実行する必要があるクリーンアップアクションを定義することを目的としています

finallyが存在する場合、「クリーンアップ」ハンドラーを指定します。 try句とexcept句を含むelse句が実行されます。いずれかの句で例外が発生し、処理されない場合、例外は一時的に保存されます。 finally句が実行されます。保存された例外がある場合は、finally句の最後で再発生します。

例:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

ご覧のとおり、finally句はどのイベントでも実行されます。 2つの文字列を分割することで発生するTypeErrorexcept句で処理されないため、finally句の実行後に再発生します。

実際のアプリケーションでは、finally節は、リソースの使用が成功したかどうかに関係なく、外部リソース(ファイルやネットワーク接続など)を解放するのに役立ちます。

2
Eugene Yarmash

Tryブロックには、tryステートメントという必須の句が1つだけあります。 except、else、finally節はオプションであり、ユーザー設定に基づいています。

finally:Pythonはtryステートメントを終了する前に、プログラムを終了している場合でも、あらゆる条件下でfinallyブロックのコードを実行します。たとえば、exceptブロックまたはelseブロックでコードを実行中にPythonでエラーが発生した場合、finallyブロックはプログラムを停止する前に実行されます。

0