为什么CPython有一个"POP_BLOCK"操作码?

mat*_*ots 9 python bytecode cpython

跟踪Python字节码中的块的目的是什么?

这里的文档提到:

...每帧,有一堆块,表示嵌套循环,try语句等.

但实际上它们似乎并不需要实际执行循环.例如,玩弄REPL我看到:

>>> def foo():
...   while True:
...     print('hi')
... 
>>> for inst in list(dis.get_instructions(foo)): print(inst)
... 
Instruction(opname='SETUP_LOOP', opcode=120, arg=12, argval=14, argrepr='to 14', offset=0, starts_line=2, is_jump_target=False)
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=2, starts_line=3, is_jump_target=True)
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='hi', argrepr="'hi'", offset=4, starts_line=None, is_jump_target=False)
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=6, starts_line=None, is_jump_target=False)
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=8, starts_line=None, is_jump_target=False)
Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=2, argval=2, argrepr='', offset=10, starts_line=None, is_jump_target=False)
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=12, starts_line=None, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=14, starts_line=None, is_jump_target=True)
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=16, starts_line=None, is_jump_target=False)
Run Code Online (Sandbox Code Playgroud)

JUMP_ABSOLUTE列出的指令跳转到LOAD_GLOBAL列出的指令.从查看说明看来,似乎SETUP_LOOPPOP_BLOCK操作码可以是无操作.

据我所知,在Python中没有块作用域变量,因此不喜欢它也是原因.

use*_*ica 5

CPython使用堆栈计算机模型,其中临时值被推送到值堆栈并由使用它们的指令弹出.当循环结束时,根据它的结束方式,它可能在值堆栈上留下不再需要的值.

帧的块堆跟踪在环和其他一些构建体的启动值堆水平的,因此该值堆栈可以恢复到环路/其他构建体后的代码所需要的堆栈是在状态.POP_BLOCK是一个将堆栈恢复到块前条目状态的构造.

块堆栈中的信息对于异常处理构造非常重要,因为当发生异常时,值栈可能处于各种奇怪的状态.它不是循环所必需的,我相信进入CPython 3.8 的补丁将消除循环的块堆栈条目,而不是让编译器静态地确定必要的处理.