職場で、私は演算子のある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で試しましたが、同じ結果です。
しかし、なぜそれは有効な構文ですか?次のいずれかだと思います。
- 本当に古いバージョンのPythonのアーティファクトです。
- 実際には
or
、except
節ます。 - これは、
except
キーワードの後に式を受け入れなければならない可能性があるPythonパーサーの制限です。
私の投票は3です(Pythonの新しいパーサーについての議論を見た場合)が、誰かがその仮説を確認できることを望んでいます。たとえば2だったら、そのユースケースを知りたいからです。
また、その探索をどのように継続するかについては、私は少し無知です。CPythonパーサーのソースコードを掘り下げる必要があると思いますが、どこにそれを見つけるか、そしてもっと簡単な方法があるかもしれませんか?