考虑以下代码:
try:
helloworld()
except:
failure()
及其反汇编(这是在Python 2.7中):
1 0 SETUP_EXCEPT 11 (to 14)
2 3 LOAD_NAME 0 (helloworld)
6 CALL_FUNCTION 0
9 POP_TOP
10 POP_BLOCK
11 JUMP_FORWARD 14 (to 28)
3 >> 14 POP_TOP
15 POP_TOP
16 POP_TOP
4 17 LOAD_NAME 1 (failure)
20 CALL_FUNCTION 0
23 POP_TOP
24 JUMP_FORWARD 1 (to 28)
27 END_FINALLY
>> 28 LOAD_CONST 0 (None)
31 RETURN_VALUE
假设
helloworld()
引发异常,则代码从地址 14 开始。因为这个 except 处理程序是通用的,所以后面跟着三个 POP_TOP 和 failure()
函数调用是有意义的。然而,之后,有一个24 JUMP_FORWARD
“跳过”27 END_FINALLY
,因此它不会被执行。它在这里的目的是什么?
我注意到版本 3.5、3.6、3.7 和 3.8 中也有类似的行为。在 3.9 中,它似乎被重命名为 RERAISE:https://godbolt.org/z/YbeqPf3nx
一些上下文:在简化了混淆的 pyc 和大量调试之后,我发现这样的结构被破坏了
uncompyle6
当
finally
块结束时,或者没有 finally
块并且没有 except
块匹配时,它会恢复异常的传播。它还可以处理恢复被 return
块暂停的 continue
或 finally
。
但是你没有
finally
,而你有一条毯子 except
,它 always 匹配,所以 END_FINALLY
永远不会运行。它可以被消除,但字节码编译器中没有处理这样做。