Python虚拟机的函数机制

来源:互联网 发布:java 对象初始化 编辑:程序博客网 时间:2024/06/06 03:53

      本文参考Python源码剖析第11章:

      python中函数就是一个对象,函数这种抽象机制是通过PyFunctionObject这个对象来实现的。

      对比PyCodeObject与PyFunctionObject前者是对一段Python源代码的静态表示,它包含了一个Code Block的静态信息,而后者是python执行def语句时动态创建的,在PyFunctionObject中也会包含一些函数的静态信息,存储在func_code中,另外还有一些函数执行时候的动态信息,比如上下文信息func_globals. 一段python代码中可能对应多个PyFunctionObject对象。

     在Python中很奇特地将函数的声明与实现分离。从虚拟机的角度看,def f()其实是函数对象的创建语句,当执行这个语句时,会动态创建一个PyFunctionObject,调用MAKE_FUNCTION,当真正调用这个函数时执行CALL_FUNCTION指令,才会去执行这个函数的字节码,具体实现过程如下:

   PyFunctionObject对象其实是起了一个搬运工的作用,在字节码MAKE_FUCTION时生成,用来包裹函数相关的字节码和名字空间以及上下文,在字节码CALL_FUNCTION时被调用,CALL_FUNCTION会的调用call_function函数把PyFunctionObject中的内容转换成一个我们熟悉的PyFramObject对象,然后进入PyEval_EvalFrameEx核心函数,开始执行函数的字节码。至此PyFunctionObject就完成了他的使命。从PyEval_EvalFrameEx开始,Python虚拟机才真正进入所谓的函数调用状态。在最终通过PyEval_EvalFrameEx时,PyFunctionObject对象的影响就已经消失,真正对新栈帧产生影响的时PyFunctionObject中存放的对字节码指令和global名字空间信息。也就是说,PyFunctionObject辛苦一场,到头来实际上是为他人做嫁衣裳,PyFunctionObject主要是对对字节码指令和global名字空间的一种打包和运输方式。


    

对于无参数函数的实现,我们来看下面的代码(这是书中的例子):

def f():

        Print “Function”

 

f()

这段代码包括了函数的定义及对函数的调用,其被编译的字节码中会的出现两个新的指令:

MAKE_FUNCTION和CALL_FUNCTION

前者在调用之前,会的有一LOAD_CONST的指令,目的是把存储在PyCodeObject中的常量表中的关于该函数具体实现的字节码入栈,然后MAKE_FUNCTION的具体实现会的把该字节码以及一些上下文信息整合起来,生成一个PyFunctionObject对象,然后把这个对象入栈。并调用STORE_NAME,把这些对象保存起来。当调用CALL_FUNCTION时,只要根据符号表找到该函数,然后找到对应的PyFunctionObject对象,对该对象抽丝剥茧并生成一个PyFramObject对象,然后就能调用PyEval_EvalFrameEx去执行具体的代码了。

值得注意的是,在函数的调用中其局部变量并没有通过常量表和符号表的方式来保存,而是直接保存在栈中


   


0 0
原创粉丝点击