Programmingバックエンド開発者

Pythonにおける例外処理のメカニズムを説明してください。try/except/finallyブロックはどのように機能し、raiseは何のために必要で、よくある落とし穴は何ですか?

Hintsage AIアシスタントで面接を突破

回答

Pythonでは、例外処理はtry/except/else/finallyの構文とraise文を使って実現されます。

tryは保護されたコードブロックを定義します。内部で例外が発生した場合、制御は最も近い適切なexceptブロックに渡されます。例外がない場合はelseブロックが実行されます。finallyブロックは、例外が発生したかどうかに関係なく常に実行されます(たとえば、リソースの解放のためです)。

raiseは明示的に例外を発生させたり、現在の例外を再発生させたりします(exceptブロック内で型を指定せずに)。

例:

try: x = 1 / 0 except ZeroDivisionError as e: print(f"エラー: {e}") else: print("エラーはありませんでした") finally: print("常に実行されます")

出力:

エラー: division by zero
常に実行されます

細かい点:

  • 入れ子になったハンドラでは、exceptの順序が重要です: 一般的な型から特定の型へ(さもなくば、特定の型は処理されません)。
  • finallyブロックは、再度の例外発生を防ぐことができません(もしfinallyraiseを挿入した場合、最も「最近の」エラーが優先されます)。

ひっかけ質問

質問: 「tryブロックとfinallyブロックの両方で異なる例外をraiseするとどうなりますか?」

回答: finally内の例外がtry内の例外を「上書き」します: finallyから発生したものが外に出ます。

def foo(): try: raise ValueError("try内で") finally: raise IndexError("finally内で") try: foo() except Exception as e: print(repr(e)) # 出力: IndexError('finally内で')

知識不足からの実際のエラーの例


物語

ETLプロセスで、finallyブロックで必ずデータベース接続を閉じていましたが、finally内で例外が発生する可能性を忘れていました(たとえば、接続が既に閉じられている場合)。結果として、finallyからの「隠れた」例外がメインコードの例外を完全に飲み込んでしまい、デバッグが急に難しくなりました。


物語

一般的なexcept Exceptionの上に特定のexceptを使ったチェーンを使用しました。その結果、すべての特定的なexceptが「死に」—低レベルの例外が個別にキャッチされず、特定のエラーの処理が難しくなりました。


物語

Webサービスで、exceptブロック内で例外を「raise」で次の処理に渡すのを忘れ、エラーをログに記録しましたが、処理を続けることを許可しました。結果として、実際のエラーが「失われ」、プログラムは不正な状態で動作し続けました。