1. ホーム
  2. python

[解決済み] Pythonでは'finally'は常に実行されるのですか?

2022-04-24 02:48:30

質問

Python の任意の try-finally ブロックに対して finally ブロックは必ず実行されますか?

たとえば、私が except ブロックを作成します。

try:
    1/0
except ZeroDivisionError:
    return
finally:
    print("Does this code run?")

をリレイズするとか。 Exception :

try:
    1/0
except ZeroDivisionError:
    raise
finally:
    print("What about this code?")

テストでは finally 上記の例では実行されますが、私が思いつかないようなシナリオもあるかと思います。

を使用するシナリオはありますか? finally ブロックは Python で実行に失敗することがありますか?

解決方法は?

の実装よりもはるかに強力な言葉です。 finally を提供します。保証されるのは、もし実行が全体の try - finally の構成では finally を実行します。保証されないのは、実行が try - finally .

  • A finally ジェネレータや非同期コルーチンでは、決して実行されないかもしれません。 もし、そのオブジェクトが最後まで実行されなかったら。このようなことが起こる可能性はたくさんありますが、ここではその一つを紹介します。

    def gen(text):
        try:
            for line in text:
                try:
                    yield int(line)
                except:
                    # Ignore blank lines - but catch too much!
                    pass
        finally:
            print('Doing important cleanup')
    
    text = ['1', '', '2', '', '3']
    
    if any(n > 1 for n in gen(text)):
        print('Found a number')
    
    print('Oops, no cleanup.')
    
    

    この例は少しトリッキーであることに注意してください。ジェネレータがガベージコレクションされるとき、Pythonは finally を投げてブロック化します。 GeneratorExit の例外が発生しますが、ここではその例外をキャッチした上で yield その時点で Python は警告 ("generator ignored GeneratorExit") を表示し、あきらめます。参照 PEP 342 (強化されたジェネレータによるコルーチン) をご覧ください。

    ジェネレータやコルーチンが結論まで実行されない他の方法としては、オブジェクトがGCされない場合(CPythonでも可能です)、あるいは async with await の中の __aexit__ である場合、またはオブジェクト await または yield の中にある finally ブロックを作成します。このリストは、すべてを網羅することを意図したものではありません。

  • A finally を実行しないかもしれません。 すべての非デーモンスレッドが先に終了した場合。

  • os._exit は直ちに処理を停止します を実行することなく finally のブロックを作成します。

  • os.fork が発生する可能性があります。 finally ブロックが 実行 2回 . 物事が2回起こることから予想される通常の問題だけでなく、共有リソースにアクセスできない場合、同時アクセスの競合(クラッシュ、ストール、...)を引き起こす可能性があります。 正しく同期させる .

    以来 multiprocessing を使うときは、fork-without-exec を使ってワーカープロセスを作成します。 フォーク 開始メソッド (Unixのデフォルト)を呼び出し、次に os._exit は、ワーカーの仕事が終わると、ワーカーの中で finallymultiprocessing の相互作用が問題になることがあります ( ).

  • Cレベルのセグメンテーションフォールトは、以下のことを防ぐことができます。 finally ブロックが実行されなくなります。
  • kill -SIGKILL を防ぐことができます。 finally ブロックが実行されないようにします。 SIGTERMSIGHUP はまた finally ブロックは、シャットダウンを制御するハンドラを自分でインストールしない限り、実行されません。 SIGTERM または SIGHUP .
  • での例外 finally は、クリーンアップの完了を妨げることがあります。特に注目すべきケースは、ユーザが control-C ちょうど を実行し始めたところで finally ブロックを作成します。Pythonは KeyboardInterrupt のすべての行をスキップします。 finally ブロックの内容を ( KeyboardInterrupt -安全なコードを書くのは非常に難しいです)。
  • コンピューターの電源が切れたり、ハイバネートして起きない場合。 finally ブロックは実行されません。

finally ブロックはトランザクションシステムではないので、アトミック性の保証やその種のものは提供しません。これらの例のいくつかは明白に思えるかもしれませんが、そのようなことが起こりうることを忘れがちで、そのような場合は finally を使うことが多いのです。