519

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
4

11 に答える 11

788

「無知なのかわかりませんが、例外を使ってフロー制御を行っているので、そういうプログラミングは好きではありません。」

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

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

さらに、try-except-style は、いくつかの「look-before-you-leap」構造に固有の競合状態を防ぐために使用されます。たとえば、os.path.existsをテストすると、使用するまでに情報が古くなっている可能性があります。同様に、Queue.fullは、古い可能性がある情報を返します。このような場合、 try-except-else スタイルはより信頼性の高いコードを生成します。

「例外はエラーではなく、例外的な状況でのみ使用する必要があることを理解しています」

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

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

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

「しかし、それが言語自体に含まれている場合、それには正当な理由があるはずですよね?」

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

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

例外の不可欠性に関する素敵なブログ投稿がここにあります。

また、このスタック オーバーフローの回答も参照してください:例外は本当に例外的なエラーですか?

「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 句の最も一般的な使用法は、少し美化することです (例外的な結果と例外的でない結果を同じレベルのインデントで揃えます)。この使用は常にオプションであり、厳密に必要というわけではありません。

于 2013-04-22T03:13:38.423 に答える
201

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

tryブロックを使用すると、予想されるエラーを処理できます。ブロックは、except処理する準備ができている例外のみをキャッチする必要があります。予期しないエラーを処理すると、コードが間違った動作をしてバグが隠れる可能性があります。

elseエラーがなければ句が実行され、ブロック内でそのコードを実行しないことで、予期tryしないエラーのキャッチを回避できます。繰り返しになりますが、予期しないエラーをキャッチすると、バグが隠れる可能性があります。

例えば:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

「try, except」スイートには、2 つの省略可能な句elsefinally. だから、実際にはtry-except-else-finally.

elsetryブロックからの例外がない場合にのみ評価されます。これにより、以下のより複雑なコードを簡素化できます。

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

elseそのため、 (バグが発生する可能性がある) 代替案と比較すると、コード行が減り、より読みやすく、保守しやすく、バグの少ないコードベースを使用できることがわかります。

finally

finally別の行が return ステートメントで評価されていても、何があっても実行されます。

疑似コードで分解

コメントを付けて、すべての機能を示す可能な限り最小の形式で、これを分解すると役立つ場合があります。この構文的に正しい (ただし、名前が定義されていないと実行できない) 疑似コードが関数内にあるとします。

例えば:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

代わりにブロック内のコードをブロック内に含めることができるのは事実ですが、例外がなければ実行されますが、そのコード自体がキャッチしている種類の例外を発生させた場合はどうなるでしょうか? ブロックにそのままにしておくと、そのバグが隠されます。elsetrytry

tryコードが失敗した場合は大声で失敗するという原則の下で、予期しない例外をキャッチしないように、ブロック内のコード行を最小限に抑えたいと考えています。これはベスト プラクティスです。

例外はエラーではないというのが私の理解です

Python では、ほとんどの例外はエラーです。

pydoc を使用して例外階層を表示できます。たとえば、Python 2 では次のようになります。

$ python -m pydoc exceptions

または Python 3:

$ python -m pydoc builtins

私たちに階層を与えます。ほとんどの種類がエラーであることがわかりますがException、Python はそれらの一部をforループの終了などに使用します ( StopIteration)。これは Python 3 の階層です。

BaseException
    Exception
        ArithmeticError
            FloatingPointError
            OverflowError
            ZeroDivisionError
        AssertionError
        AttributeError
        BufferError
        EOFError
        ImportError
            ModuleNotFoundError
        LookupError
            IndexError
            KeyError
        MemoryError
        NameError
            UnboundLocalError
        OSError
            BlockingIOError
            ChildProcessError
            ConnectionError
                BrokenPipeError
                ConnectionAbortedError
                ConnectionRefusedError
                ConnectionResetError
            FileExistsError
            FileNotFoundError
            InterruptedError
            IsADirectoryError
            NotADirectoryError
            PermissionError
            ProcessLookupError
            TimeoutError
        ReferenceError
        RuntimeError
            NotImplementedError
            RecursionError
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

コメント投稿者は次のように尋ねました。

外部 API に ping を実行するメソッドがあり、API ラッパーの外部のクラスで例外を処理したいとします。e が例外オブジェクトである except 節の下でメソッドから単に e を返しますか?

いいえ、例外を返さないでくださいraise。スタックトレースを保持するためにベアで再発生させてください。

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

または、Python 3 では、新しい例外を発生させ、例外チェーンでバックトレースを保持できます。

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

ここで私の答えを詳しく説明します。

于 2015-07-25T13:30:17.183 に答える
16

Pythonでtry-except-elseを使用するのは良い習慣ですか?

これに対する答えは、コンテキストに依存するということです。これを行う場合:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

これは、あなたが Python をよく知らないことを示しています。この機能はdict.getメソッドにカプセル化されています。

item = d.get('item', 'default')

try/ブロックは、exceptアトミック メソッドを使用して 1 行で効率的に実行できるものを記述する、はるかに視覚的に雑然とした冗長な方法です。これが真実である他のケースがあります。

ただし、だからといってすべての例外処理を避けるべきだというわけではありません。場合によっては、競合状態を回避することが推奨されます。ファイルが存在するかどうかを確認せずに、ファイルを開こうとして、適切な IOError をキャッチします。単純さと読みやすさのために、これをカプセル化するか、適切に分解してみてください。

Zen of Pythonを読んで、緊張関係にある原則があることを理解し、その中のいずれかのステートメントに過度に依存するドグマに注意してください。

于 2015-02-03T16:54:25.177 に答える
6

これを見るたびに:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

またはこれでも:

try:
    return 1 / x
except ZeroDivisionError:
    return None

代わりにこれを考慮してください:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x
于 2017-01-13T23:40:49.893 に答える
-4

OP, YOU ARE CORRECT. The else after try/except in Python is ugly. it leads to another flow-control object where none is needed:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

A totally clear equivalent is:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

This is far clearer than an else clause. The else after try/except is not frequently written, so it takes a moment to figure what the implications are.

Just because you CAN do a thing, doesn't mean you SHOULD do a thing.

Lots of features have been added to languages because someone thought it might come in handy. Trouble is, the more features, the less clear and obvious things are because people don't usually use those bells and whistles.

Just my 5 cents here. I have to come along behind and clean up a lot of code written by 1st-year out of college developers who think they're smart and want to write code in some uber-tight, uber-efficient way when that just makes it a mess to try and read / modify later. I vote for readability every day and twice on Sundays.

于 2015-03-20T16:03:11.920 に答える