到目前位置,我们已经接触了几次函数的创建和调用过程,对应的是Python虚拟机中的MAKE_FUNCTIONCALL_FUNCTION 指令。

MAKE_FUNCTION

MAKE_FUNCTION并不是单独出现的, 他的上下文通常是这样,上游会把两个值 -- 分别是code object和函数名压栈,下游会把MAKE_FUNCTION压栈的东西(函数对象)出栈,并存到名字中。

  2           4 LOAD_CONST               1 (<code object foo at 0x1103334b0, file "func.py", line 2>)
              6 LOAD_CONST               2 ('foo')
              8 MAKE_FUNCTION            0
             10 STORE_NAME               1 (foo)

我们主要分析ceval.c中相关代码

// File: cpython/Python/ceval.c
// ...
TARGET(MAKE_FUNCTION) {
PyObject *qualname = POP();
PyObject *codeobj = POP();
PyFunctionObject *func = (PyFunctionObject *)
PyFunction_NewWithQualName(codeobj, f->f_globals, qualname);
// ...
PUSH((PyObject *)func);
// ...

MAKE_FUNCTION指令从Value Stack弹出两个object,第一个被弹出的是汇编指令LOAD的'foo',这被当做是 qualname,也就是我们函数名,第二个弹出的是codeobj,也就是代码对象。接着调用了构建函数对象的C函数:PyFunction_NewWithQualName,创建了一个函数对象,在做了一堆逻辑判断后,把函数对象压栈到Value Stack中。

创建函数对象 PyFunction_NewWithQualName

Python中,函数也是一个对象,定义在 cpython/Include/funcobject.h 中。

typedef struct {
    PyObject_HEAD
    PyObject *func_code;        /* A code object, the __code__ attribute */
    PyObject *func_globals;     /* A dictionary (other mappings won't do) */
    // ...
    PyObject *func_closure;     /* NULL or a tuple of cell objects */
    PyObject *func_doc;         /* The __doc__ attribute, can be anything */
    PyObject *func_name;        /* The __name__ attribute, a string object */
    // ...
    PyObject *func_qualname;    /* The qualified name */
} PyFunctionObject;

PyFunctionObject大致分为两块

  • 第一部分是像 func_code,func_globals等成员变量,存储了函数运行所需要的代码、参数信息
  • 第二部分是x像 func_doc, func_name等成员变量,存储了函数的函数名、说明文档等基本meta信息

其中func_closure是函数的闭包,用来维护函数局部变量和全局变量关系,关于闭包我们会在后面单独展开。

Python3中具体的函数创建流程如下图所示,首先根据函数的代码结构,创建一个Code_Object,接着调用MAKE_FUNCTION 绑定具体的函数名,生成Function_Object,最后存到函数名hash表中。

results matching ""

    No results matching ""