Python で try-except-else を使用するのは良い習慣でしょうか? 質問する

Python で try-except-else を使用するのは良い習慣でしょうか? 質問する

Python では、次のようなブロックを時々見かけます:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

try-except-else が存在する理由は何ですか?

例外を使用してフロー制御を実行するため、そのようなプログラミングは好きではありません。ただし、それが言語に含まれている場合は、それには十分な理由があるはずです。

私の理解では、例外はエラーではなく、例外的な状況(たとえば、ファイルをディスクに書き込もうとしたが、空き容量がない、または権限がないなど)にのみ使用し、フロー制御には使用しないでください。

通常、例外は次のように処理します。

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

または、例外が発生した場合に何も返したくない場合は、次のようにします。

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

ベストアンサー1

「無知から来るものかどうかは分かりませんが、例外を使用してフロー制御を実行するようなプログラミングは好きではありません。」

Python の世界では、フロー制御に例外を使用するのは一般的で普通のことです。

Pythonのコア開発者でさえフロー制御に例外を使用しており、そのスタイルは言語に深く組み込まれています(つまり、イテレータプロトコルは反復停止ループ終了を通知します。

さらに、try-exceptスタイルは、いくつかのコードに固有の競合状態を防ぐために使用されます。"石橋を叩いて渡る"構造。例えば、テストos.pathが存在するその結果、使用する頃には情報が古くなっている可能性があります。同様に、キューがいっぱい古くなった情報を返す可能性があります。try-except-else スタイルこのような場合には、より信頼性の高いコードが生成されます。

「私の理解では、例外はエラーではなく、例外的な状況にのみ使用されるべきです」

他の言語では、そのルールは、ライブラリに反映されている文化的規範を反映しています。また、「ルール」は、それらの言語のパフォーマンスに関する考慮事項にも一部基づいています。

Pythonの文化的規範は多少異なります。多くの場合、制御フローには例外を使用する必要があります。また、Pythonで例外を使用すると、一部のコンパイル言語のように周囲のコードと呼び出し元のコードが遅くなることもありません(つまり、Python の実際に例外を使用するかどうかに関係なく、各ステップで例外チェックを行うコードがすでに実装されています。

言い換えれば、「例外は例外的なものに限る」というあなたの理解は、他の言語では意味のあるルールですが、Python では意味がありません。

「しかし、言語自体に含まれているのであれば、それにはちゃんとした理由があるはずですよね?」

例外は競合状態を回避するのに役立つだけでなく、エラー処理をループの外側に引き出すのにも非常に便利です。これは、自動処理を持たないインタプリタ言語では必要な最適化です。ループ不変コードモーション

また、問題の処理能力が問題の発生場所から遠く離れている一般的な状況では、例外によってコードをかなり簡素化できます。たとえば、トップレベルのユーザー インターフェイス コードがビジネス ロジックのコードを呼び出し、そのコードが低レベルのルーチンを呼び出すことはよくあります。低レベルのルーチンで発生する状況 (データベース アクセスでの一意のキーの重複レコードなど) は、トップレベルのコードでのみ処理できます (既存のキーと競合しない新しいキーをユーザーに要求するなど)。この種の制御フローに例外を使用すると、中間レベルのルーチンは問題を完全に無視し、フロー制御のその側面から適切に分離できます。

そこには例外の不可欠性に関する素晴らしいブログ記事はこちら

また、この Stack Overflow の回答もご覧ください。例外は本当に例外的なエラーに対するものなのでしょうか?

「try-except-else が存在する理由は何ですか?」

else 節自体は興味深いものです。これは、例外がないが finally 節の前に実行されるものです。これが主な目的です。

else 節がない場合、ファイナライズ前に追加のコードを実行する唯一のオプションは、try 節にコードを追加するという不器用な方法になります。これは、try ブロックによって保護されることを意図していないコードで例外が発生するリスクがあるため、不器用です。

ファイナライズ前に追加の保護されていないコードを実行するユースケースは、あまり頻繁には発生しません。したがって、公開されたコードに多くの例が見られることは期待しないでください。これはかなりまれです。

else 節のもう 1 つの使用例は、例外が発生しないときに必ず実行され、例外が処理されるときには実行されないアクションを実行することです。例:

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

別の例は、ユニットテスト ランナーで発生します。

try:
    tests_run += 1
    run_testcase(case)
except Exception:
    tests_failed += 1
    logging.exception('Failing test case: %r', case)
    print('F', end='')
else:
    logging.info('Successful test case: %r', case)
    print('.', end='')

最後に、try ブロック内で else 節を使用する最も一般的な方法は、見た目を少し整えることです (例外的な結果と例外的でない結果を同じインデント レベルに揃える)。この使用は常にオプションであり、必ずしも必要ではありません。

おすすめ記事