コードを書くとき、私はしばしば次のようなことをしたいです:
_try:
foo()
except FooError:
handle_foo()
else:
try:
bar()
except BarError:
handle_bar()
else:
try:
baz()
except BazError:
handle_baz()
else:
qux()
finally:
cleanup()
_
明らかに、これは完全に判読できません。しかし、それは比較的単純なアイデアを表しています。それぞれの例外ハンドラーを使用して一連の関数(または短いコードスニペット)を実行し、関数が失敗するとすぐに停止します。 Pythonがこのコードに構文糖を提供する可能性があると思います。おそらくこのようなものです:
_# NB: This is *not* valid Python
try:
foo()
except FooError:
handle_foo()
# GOTO finally block
else try:
bar()
except BarError:
handle_bar()
# ditto
else try:
baz()
except BazError:
handle_baz()
# ditto
else:
qux()
finally:
cleanup()
_
例外が発生しない場合、これはfoo();bar();baz();qux();cleanup()
と同等です。例外が発生した場合、それらは適切な例外ハンドラー(存在する場合)によって処理され、cleanup()
にスキップします。特に、bar()
がFooError
またはBazError
を発生させる場合、例外はnotがキャッチされ、呼び出し元に伝達されます。これは望ましいことなので、本当に処理することを期待している例外のみをキャッチします。
構文の醜さに関係なく、この種のコードは一般的に単に悪い考えですか?もしそうなら、それをどのようにリファクタリングしますか?コンテキストマネージャーを使用して複雑さの一部を吸収できると思いますが、それが一般的なケースでどのように機能するかは本当にわかりません。
try:
foo()
except FooError:
handle_foo()
else:
...
finally:
cleanup()
handle_foo
は何をしますか?例外処理ブロックで通常行うことはいくつかあります。
with
で最適に処理されます例外処理でおかしなことをしているようです。ここでの質問は、例外を異常な方法で使用することに関する単純な症状です。あなたは典型的なパターンに陥っていません、そしてそれがこれが厄介になった理由です。
これらのhandle_
関数で何をしているのかをよく理解していなければ、私が言えることはそれだけです。
戻る前に処理する必要のある例外をスローする一連のコマンドがあるようです。コードと例外処理を別々の場所にグループ化してみてください。私はこれがあなたの意図することをすると信じています。
try:
foo()
bar()
baz()
qux()
except FooError:
handle_foo()
except BarError:
handle_bar()
except BazError:
handle_baz()
finally:
cleanup()
必要に応じて、いくつかの方法があります。
これがループの方法です:
try:
for func, error, err_handler in (
(foo, FooError, handle_foo),
(bar, BarError, handle_bar),
(baz, BazError, handle_baz),
):
try:
func()
except error:
err_handler()
break
finally:
cleanup()
以下は、error_handlerの後に終了する方法です。
def some_func():
try:
try:
foo()
except FooError:
handle_foo()
return
try:
bar()
except BarError:
handle_bar()
return
try:
baz()
except BazError:
handle_baz()
return
else:
qux()
finally:
cleanup()
個人的にはループ版の方が読みやすいと思います。
まず、with
を適切に使用すると、多くの例外処理コードを削減または排除できるため、保守性と可読性の両方が向上します。
これで、さまざまな方法でネストを減らすことができます。他のポスターはすでにいくつか提供していますので、ここに私自身のバリエーションがあります:
for _ in range(1):
try:
foo()
except FooError:
handle_foo()
break
try:
bar()
except BarError:
handle_bar()
break
try:
baz()
except BazError:
handle_baz()
break
qux()
cleanup()