На работе я наткнулся на 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
Так что он не уловил второе исключение, и, глядя на байт-код, становится понятнее, почему:
>>> 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, и, наконец, сразу переходит к инструкции 20, где exception match
выполняется. Поскольку инструкция 18 была пропущена, KeyError
она никогда не загружалась в стек и поэтому не совпадает.
Я пытался с Python 2.7 и 3.6, тот же результат.
Но тогда, почему это правильный синтаксис? Я представляю, что это одно из следующего:
- Это артефакт из действительно старой версии Python.
- На самом деле есть действительный вариант использования для использования
or
вexcept
предложении. - Это просто ограничение синтаксического анализатора Python, который может принимать любое выражение после
except
ключевого слова.
Мой голос за 3 (учитывая, что я видел некоторую дискуссию о новом парсере для Python), но я надеюсь, что кто-то может подтвердить эту гипотезу. Потому что, если это было, например, 2, я хочу знать этот вариант использования!
Кроме того, я немного не понимаю, как мне продолжить это исследование. Я полагаю, мне нужно было бы покопаться в исходном коде парсера CPython, но не знаю, где его найти, и, возможно, есть более простой способ?