编译自:http://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html 作者:Taavi Burns 翻译:鸿 如有翻译问题或建议,请公众号留言
Frames
目前可以确认Python虚拟机是一个堆栈机器。它通过指令来控制执行顺序,推入和弹出堆栈的值。在上面的例子中,最后一条指令是RETURN_VALUE,它对应于代码中的return语句。但指令返回到哪里呢?
要回答这个问题,必须增加一层结构:frame。frame是有关代码信息和上下文的集合。随着Python代码的执行,frame会随时创建和销毁。每个函数调用都会有一个对应的frame,所以每个frame都有一个与之关联的代码对象时,代码对象可以有多个frame。如果有一个递归调用自己10次的函数,就会有11个frame(每个递归级别拥有一个,另外一个是用于你的module)。通常,Python程序中的每个域都有一个frame。例如,module,函数调用和类定义都会有一个frame。 frame位于call stack上(call stack和平常的堆栈一样)。解释器在执行字节码时操作的堆栈被称为data stack。还有第三个堆栈,称为block stack。block用于特定的控制流,例如循环和异常处理。call stack中的每个frame都有自己的data stack和block stack。
代码语言:javascript复制>>> def bar(y):
... z = y 3 # <--- (3) ... and the interpreter is here.
... return z
...
>>> def foo():
... a = 1
... b = 2
... return a bar(b) # <--- (2) ... which is returning a call to bar ...
...
>>> foo() # <--- (1) We're in the middle of a call to foo ...
3
此时,解释器正处于函数调用的中间。调用堆栈有三个frame:一个用于模块级别,一个用于函数foo,另一个用于bar。一旦bar返回,与其关联的frame就会弹出call stack并被丢弃。字节码RETURN_VALUE会告诉解释器需要在frame之间传递一个值。这时它会将推出call stack顶层的frame的data stack的顶层值。再将整个frame从call stack中弹出并传递出去。最后,将这个值压入下一个frame的data stack中。
Byterun刚开始时在整个虚拟机上只有一个data stack,而不是在每个frame上都有一个data stack。直到遇上了生成器。最后,通过仔细阅读CPython源码,意识到了错误,之后将data stacke移动到每个frame上解决了问题。