You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

旧版Python中END_FINALLY字节码的作用及跳转疑问

关于Python 2.7中END_FINALLY字节码作用的疑问

请考虑以下代码:

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,使其不会被执行。请问该END_FINALLY字节码的作用是什么?

我注意到Python 3.5、3.6、3.7和3.8版本中存在类似行为,而Python 3.9中该字节码被重命名为RERAISE。

背景信息:在简化一个混淆的pyc文件并经过大量调试后,我发现此类结构会破坏uncompyle6工具。


回答:

END_FINALLY字节码的核心作用是处理异常未被当前except块完全捕获的场景,具体细节如下:

  1. 处理异常重抛场景:如果failure()函数本身抛出异常,或者你在except块内显式用raise重新抛出原异常,程序不会执行JUMP_FORWARD跳转,而是会走到END_FINALLY。这个字节码会负责清理异常栈,并将异常传递到上层调用栈中。

  2. 复用统一的异常处理框架:Python的try/except和try/finally共享底层字节码结构,SETUP_EXCEPT指令同时支持两种场景。END_FINALLY原本是为finally块设计的收尾逻辑,在这里被保留是为了统一异常处理流程——当except块无法消化异常时,它就承担起重新抛出异常的职责。

Python 3.9将其重命名为RERAISE,其实是更精准地反映了它在这种except场景下的实际作用:当异常未被当前块处理时,负责重新抛出异常。

你遇到的uncompyle6失效问题,大概率是混淆pyc文件篡改了正常的跳转逻辑,导致反编译器无法识别END_FINALLY对应的分支场景,从而无法正确还原源代码结构。


内容的提问来源于stack exchange,提问作者musava_ribica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 06:40:55