python3.6 源码分析(三):创建函数

python定义函数的语句十分简单:

              0 LOAD_CONST               0 (<code object fun at 0x000001C7DFA48ED0, file "", line 1>)
              2 LOAD_CONST               1 ('fun')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (fun)

首先加载codeobject到栈顶,然后加载函数名,MAKE_FUNCTION就完了,这看似简单的背后其实python做了大量的工作,一方面是函数内的语句的编译,还有就是各种名字空间的绑定,比如闭包。
让我们通过python源码看看函数究竟是怎么构造出来的,从MAKE_FUNCTION入手看看这条字节码的实现:

TARGET(MAKE_FUNCTION) {
            PyObject *qualname = POP();
            PyObject *codeobj = POP();
            PyFunctionObject *func = (PyFunctionObject *)
                PyFunction_NewWithQualName(codeobj, f->f_globals, qualname);

            Py_DECREF(codeobj);
            Py_DECREF(qualname);
            if (func == NULL) {
                goto error;
            }

            if (oparg & 0x08) {
                assert(PyTuple_CheckExact(TOP()));
                func ->func_closure = POP();
            }
            if (oparg & 0x04) {
                assert(PyDict_CheckExact(TOP()));
                func->func_annotations = POP();
            }
            if (oparg & 0x02) {
                assert(PyDict_CheckExact(TOP()));
                func->func_kwdefaults = POP();
            }
            if (oparg & 0x01) {
                assert(PyTuple_CheckExact(TOP()));
                func->func_defaults = POP();
            }

            PUSH((PyObject *)func);
            DISPATCH();
        }

先不看那些if判断,主要的流程就是PyFunction_NewWithQualName这个函数了,至于那些if,初步判断应该是根据MAKE_FUNCTION的参数取栈顶数据函数对象相应的域,具体是什么目前不得而知。
好吧,先看PyFunction_NewWithQualName:

PyObject *
PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
{
    PyFunctionObject *op;
    PyObject *doc, *consts, *module;
    static PyObject *__name__ = NULL;

    if (__name__ == NULL) {
        __name__ = PyUnicode_InternFromString("__name__");
        if (__name__ == NULL)
            return NULL;
    }

    op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);
    if (op == NULL)
        return NULL;

    op->func_weakreflist = NULL;
    Py_INCREF(code);
    op->func_code = code;
    Py_INCREF(globals);
    op->func_globals = globals;
    op->func_name = ((PyCodeObject *)code)->co_name;
    Py_INCREF(op->func_name);
    op->func_defaults = NULL; /* No default arguments */
    op->func_kwdefaults = NULL; /* No keyword only defaults */
    op->func_closure = NULL;

    consts = ((PyCodeObject *)code)->co_consts;
    if (PyTuple_Size(consts) >= 1) {
        doc = PyTuple_GetItem(consts, 0);
        if (!PyUnicode_Check(doc))
            doc = Py_None;
    }
    else
        doc = Py_None;
    Py_INCREF(doc);
    op->func_doc = doc;

    op->func_dict = NULL;
    op->func_module = NULL;
    op->func_annotations = NULL;

    /* __module__: If module name is in globals, use it.
       Otherwise, use None. */
    module = PyDict_GetItem(globals, __name__);
    if (module) {
        Py_INCREF(module);
        op->func_module = module;
    }
    if (qualname)
        op->func_qualname = qualname;
    else
        op->func_qualname = op->func_name;
    Py_INCREF(op->func_qualname);

    _PyObject_GC_TRACK(op);
    return (PyObject *)op;
}

当然这么长的代码考虑到了定义函数的所有情况,而我们只是定义了一个最简单的无参函数,所以最重要的无非是code和globals这两个域了,看看他们的出处,code不用说了,就是函数体编译成的codeobejct,那么globals呢,我们往上回溯,参数来自f->f_globals这个东西,再往上找f的定义,

_PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)

其实就是当前栈帧咯,也就是说,定义一个函数,这个函数的globals名字空间就是当前栈帧的名字空间,以后不论函数在哪里被调用,都能访问到当前globals下的名字,厉害吧。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值