except句内で `or`を使用するとSyntaxErrorが発生しないのはなぜですか?有効な用途はありますか?


11

職場で、私は演算子のあるexcept節に偶然出会いましたor

try:
    # Do something.
except IndexError or KeyError:
    # ErrorHandling

例外クラスをタプルとして渡す必要があることはわかっていますが、それによってが発生しないこともありましたSyntaxError

最初に、それが実際に機能するかどうかを調査したいと思いました。そして、そうではありません。

>>> def with_or_raise(exc):
...     try:
...         raise exc()
...     except IndexError or KeyError:
...         print('Got ya!')
...

>>> with_or_raise(IndexError)
Got ya!

>>> with_or_raise(KeyError)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in with_or_raise
KeyError

したがって、2番目の例外はキャッチされず、バイトコードを見ると、その理由がより明確になります。

>>> import dis
>>> dis.dis(with_or_raise)
  2           0 SETUP_EXCEPT            10 (to 12)

  3           2 LOAD_FAST                0 (exc)
              4 CALL_FUNCTION            0
              6 RAISE_VARARGS            1
              8 POP_BLOCK
             10 JUMP_FORWARD            32 (to 44)

  4     >>   12 DUP_TOP
             14 LOAD_GLOBAL              0 (IndexError)
             16 JUMP_IF_TRUE_OR_POP     20
             18 LOAD_GLOBAL              1 (KeyError)
        >>   20 COMPARE_OP              10 (exception match)
             22 POP_JUMP_IF_FALSE       42
             24 POP_TOP
             26 POP_TOP
             28 POP_TOP

  5          30 LOAD_GLOBAL              2 (print)
             32 LOAD_CONST               1 ('Got ya!')
             34 CALL_FUNCTION            1
             36 POP_TOP
             38 POP_EXCEPT
             40 JUMP_FORWARD             2 (to 44)
        >>   42 END_FINALLY
        >>   44 LOAD_CONST               0 (None)
             46 RETURN_VALUE

ご覧のとおり、命令14は最初にIndexErrorクラスをスタックにロードします。次に、その値がTrueであるかどうかをチェックします。これはPythonの真実性によるものであり、最後にexception matchが行われる命令20に直接ジャンプします。命令18がスキップKeyErrorされたため、スタックにロードされなかったため、一致しません。

私はPython 2.7と3.6で試しましたが、同じ結果です。

しかし、なぜそれは有効な構文ですか?次のいずれかだと思います。

  1. 本当に古いバージョンのPythonのアーティファクトです。
  2. 実際にはorexcept節ます。
  3. これは、exceptキーワードの後に式を受け入れなければならない可能性があるPythonパーサーの制限です。

私の投票は3です(Pythonの新しいパーサーについての議論を見た場合)が、誰かがその仮説を確認できることを望んでいます。たとえば2だったら、そのユースケースを知りたいからです。

また、その探索をどのように継続するかについては、私は少し無知です。CPythonパーサーのソースコードを掘り下げる必要があると思いますが、どこにそれを見つけるか、そしてもっと簡単な方法があるかもしれませんか?

回答:


7

ではexcept ee任意の有効なPython式を使用できます。

try1_stmt ::=  "try" ":" suite
               ("except" [expression ["as" identifier]] ":" suite)+
               ...

[..] except式を持つ句の場合、その式が評価され、結果のオブジェクトが例外と「互換性がある」場合、句は例外と一致します。オブジェクトは、例外オブジェクトのクラスまたは基本クラス、または例外と互換性のあるアイテムを含むタプルである場合、例外と互換性があります。

https://docs.python.org/3/reference/compound_stmts.html#the-try-statement

IndexError or KeyErrorは値を生成しますIndexError。したがって、これは次と同等です。

except IndexError:
   ...

あの速い答えをありがとう!パーサーの制限(つまり、より具体的な式ではなく、任意の式のみを受け入れることができる)と、物事を制限しすぎないようにするための意図的な選択のどちらを考慮しますか?
ロイック・テイシェイラ

1
私には、シンプルなほうが優れているというPythonの基本原則に固執するように思えます。何らかの表現を受け入れるときに、新しい特別なケースがなくても完全な自由を意味するのに、制限するだけの新しいルールを作成するのはなぜですか?
だます

これは意図的な選択であり、動的にキャッチする例外のタプルをアセンブルし、そのような値をexceptステートメントで使用することを可能にします。
user4815162342

可能性を制限する理由は、開発者が意図したことをしないコードを書くのを防ぐことだと思います。結局のところ、書くことexcept IndexError or KeyErrorはまともなもののように見えます。しかし、Pythonが尊重しようとする他のいくつかの価値観に反することは、あなたに同意します。
ロイック・テイシェイラ

Pythonではも許可されていることに注意してくださいvar == 1 or 2。これは、訓練されていない人にとっては、「書くのにまともなもののようにも見えます」。
Eric

-2

論理式(最初の偽でない要素を返すだけ)ではなく、n組の型を使用する必要があります。

def with_or_raise(exc):
  try:
    raise exc()
  except (IndexError,KeyError):
    print('Got ya!')

質問で述べたように、例外クラスをタプルとして渡す必要があることはわかっていますが、使用orがなぜまだ有効なPythonなのか疑問に思っていました。
ロイック・テイシェイラ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.