在工作中,我偶然发现了一个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解析器的源代码,但是在idk哪里可以找到它,也许还有更简单的方法?