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

时间:2018-05-09 17:37:42

标签: python bytecode cpython

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

文档here提及:

  

...每帧,有一堆块,表示嵌套循环,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)

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

根据我的理解,在Python中没有块范围的变量,所以它不是这样的原因。

2 个答案:

答案 0 :(得分:5)

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

框架的块堆栈在循环开始时和其他一些构造中跟踪值堆栈级别,因此值堆栈可以恢复到循环/其他构造需要堆栈之后的代码的状态要进入。POP_BLOCK是将堆栈恢复到块前进入状态的构造之一。

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

答案 1 :(得分:1)

示例函数中的SETUP_LOOPPOP_BLOCK字节码是无用的,因为循环会永远运行,但如果循环内部有break语句,那么它们在框架将被使用。解释器会在发生BREAK_LOOP语句的地方放置break字节码,它会使用块信息来查找最接近的循环。

请注意this part of the bytecode is apparently going to change in Python 3.8,因此您可能不想投入太多精力来了解它目前的工作方式。您可以在Python错误跟踪器上阅读issue 17611,以了解在实施之前如何讨论该主题。