Python 源码分析之函数机制

版权声明:本文系原创,转发请注明出处,商业用途联系作者 https://blog.csdn.net/wenxueliu/article/details/80920026

在 python 中函数也是一个对象

typedef struct {
    PyObject_HEAD
    PyObject *func_code;    /*  函数编译之后的 PyCodeObject, the __code__ attribute */
    PyObject *func_globals; /* A dictionary (other mappings won't do) */
    PyObject *func_defaults;    /* NULL or a tuple */
    PyObject *func_kwdefaults;  /* NULL or a dict */
    PyObject *func_closure; /* NULL or a tuple of cell objects */
    PyObject *func_doc;     /* The __doc__ attribute, 函数编译之后,代码段的第一个常量  */
    PyObject *func_name;    /* The __name__ attribute, a string object */
    PyObject *func_dict;    /* The __dict__ attribute, a dict or NULL */
    PyObject *func_weakreflist; /* List of weak references */
    PyObject *func_module;  /* The __module__ attribute, can be anything */
    PyObject *func_annotations; /* Annotations, a dict or NULL */
    PyObject *func_qualname;    /* The qualified name */

    /* Invariant:
     *     func_closure contains the bindings for func_code->co_freevars, so
     *     PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
     *     (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
     */
} PyFunctionObject;

PyCodeObject 是一个静态代码块的描述,说静态是应该在编译的时候就可以知道该
代码块有哪些内容。比如常量表,符号表,局部变量等等,我们一般都针对的是
PyCodeObject。

而 PyFunctionObject 是动态的,这个对象是定义 def 的函数之后,每个一个函数
都会对应一个 PyFunctionObject,其中 func_code 保存的就是该函数的代码相关
部分,除此之外,还有 globals, default 等等,一般是动态决定的。 还要需要
注意的是,对于一个函数 f,在运行过程中,可能在多处调用,就会创建多个
PyFunctionObject 对象,而每个对象的 func_code 都指向同一 PyCodeObject。

所以,对于一个 PyFunctionObject 它既包含静态部分,如 func_code, 也包含
动态部分,比如函数参数。

#!/usr/bin/env python
# encoding: utf-8

def test():
    a = 1

test()

编译之后

magic b'330d0d0a'
moddate b'c075345b' (Thu Jun 28 13:44:32 2018)
files sz 604
code
   argcount 0
   nlocals 0
   stacksize 2
   flags 0040
   code b'6400640184005a0065008300010064025300'
  4           0 LOAD_CONST               0 (<code object test at 0x10ca48ae0, file "/tmp/a.py", line 4>)
              2 LOAD_CONST               1 ('test')
              4 MAKE_FUNCTION            0           //创建 PyFunctionObject 并入栈
              6 STORE_NAME               0 (test)    //将 PyFunctionObject 保存到 test

  7           8 LOAD_NAME                0 (test)
             10 CALL_FUNCTION            0
             12 POP_TOP
             14 LOAD_CONST               2 (None)
             16 RETURN_VALUE
   consts
      code
         argcount 0
         nlocals 1
         stacksize 1
         flags 0043
         code b'64017d0064005300'
  5           0 LOAD_CONST               1 (1)
              2 STORE_FAST               0 (a)
              4 LOAD_CONST               0 (None)
              6 RETURN_VALUE
         consts
            None
            1
         names ()
         varnames ('a',)
         freevars ()
         cellvars ()
         filename '/tmp/a.py'
         name 'test'
         firstlineno 4
         lnotab b'0001'
      'test'
      None
   names ('test',)
   varnames ()
   freevars ()
   cellvars ()
   filename '/tmp/a.py'
   name '<module>'
   firstlineno 4
   lnotab b'0803'

由编译可知产生两个代码段,一个是 a.py 对应的 PyCodeObject,一个是函数 test 对应的 PyCodeObject。而 test 对应的 PyCodeObject 是一个常量。

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;
}

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();
    }

    //当前函数对应的 PyFunctionObject 入栈
    PUSH((PyObject *)func);
    DISPATCH();
}

创建一个 PyFunctionObject 对象,其 globals, 都来自外部的 PyFrameObject

static PyObject *
call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
{
    //执行函数,从栈指针定位到函数
    PyObject **pfunc = (*pp_stack) - oparg - 1;
    PyObject *func = *pfunc;
    PyObject *x, *w;
    Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
    //nargs 参数个数
    Py_ssize_t nargs = oparg - nkwargs;
    PyObject **stack;

    /* Always dispatch PyCFunction first, because these are
       presumed to be the most frequent callable object.
    */
    //C 函数
    if (PyCFunction_Check(func)) {
        PyThreadState *tstate = PyThreadState_GET();

        PCALL(PCALL_CFUNCTION);

        stack = (*pp_stack) - nargs - nkwargs;
        C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
    }
    else {
        if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
            /* optimize access to bound methods */
            PyObject *self = PyMethod_GET_SELF(func);
            PCALL(PCALL_METHOD);
            PCALL(PCALL_BOUND_METHOD);
            Py_INCREF(self);
            func = PyMethod_GET_FUNCTION(func);
            Py_INCREF(func);
            Py_SETREF(*pfunc, self);
            nargs++;
        }
        else {
            Py_INCREF(func);
        }

        stack = (*pp_stack) - nargs - nkwargs;

        if (PyFunction_Check(func)) {
            x = fast_function(func, stack, nargs, kwnames);
        }
        else {
            x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
        }

        Py_DECREF(func);
    }

    assert((x != NULL) ^ (PyErr_Occurred() != NULL));

    /* Clear the stack of the function object.  Also removes
       the arguments in case they weren't consumed already
       (fast_function() and err_args() leave them on the stack).
     */
    while ((*pp_stack) > pfunc) {
        w = EXT_POP(*pp_stack);
        Py_DECREF(w);
        PCALL(PCALL_POP);
    }

    return x;
}
TARGET(CALL_FUNCTION) {
    PyObject **sp, *res;
    PCALL(PCALL_ALL);
    sp = stack_pointer;
    res = call_function(&sp, oparg, NULL);
    stack_pointer = sp;
    PUSH(res);
    if (res == NULL) {
        goto error;
    }
    DISPATCH();
}

//Object/methodobject.c
PyObject *
_PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack,
                              Py_ssize_t nargs, PyObject *kwnames)
{
    PyObject *kwdict, *result;
    Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);

    assert(PyCFunction_Check(func));
    assert(nargs >= 0);
    assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
    assert((nargs == 0 && nkwargs == 0) || stack != NULL);
    /* kwnames must only contains str strings, no subclass, and all keys must
       be unique */

    if (nkwargs > 0) {
        kwdict = _PyStack_AsDict(stack + nargs, kwnames);
        if (kwdict == NULL) {
            return NULL;
        }
    }
    else {
        kwdict = NULL;
    }

    result = _PyCFunction_FastCallDict(func, stack, nargs, kwdict);
    Py_XDECREF(kwdict);
    return result;
}

#define PyCFunction_GET_FUNCTION(func) \
        (((PyCFunctionObject *)func) -> m_ml -> ml_meth)

PyObject *
_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
                          PyObject *kwargs)
{
    PyCFunctionObject *func = (PyCFunctionObject*)func_obj;
    PyCFunction meth = PyCFunction_GET_FUNCTION(func);
    PyObject *self = PyCFunction_GET_SELF(func);
    PyObject *result;
    int flags;

    assert(PyCFunction_Check(func));
    assert(func != NULL);
    assert(nargs >= 0);
    assert(nargs == 0 || args != NULL);
    assert(kwargs == NULL || PyDict_Check(kwargs));

    /* _PyCFunction_FastCallDict() must not be called with an exception set,
       because it may clear it (directly or indirectly) and so the
       caller loses its exception */
    assert(!PyErr_Occurred());

    flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST);

    switch (flags)
    {
    case METH_NOARGS:
        if (kwargs != NULL && PyDict_Size(kwargs) != 0) {
            PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
                         func->m_ml->ml_name);
            return NULL;
        }

        if (nargs != 0) {
            PyErr_Format(PyExc_TypeError,
                "%.200s() takes no arguments (%zd given)",
                func->m_ml->ml_name, nargs);
            return NULL;
        }

        result = (*meth) (self, NULL);
        break;

    case METH_O:
        if (kwargs != NULL && PyDict_Size(kwargs) != 0) {
            PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
                         func->m_ml->ml_name);
            return NULL;
        }

        if (nargs != 1) {
            PyErr_Format(PyExc_TypeError,
                "%.200s() takes exactly one argument (%zd given)",
                func->m_ml->ml_name, nargs);
            return NULL;
        }

        result = (*meth) (self, args[0]);
        break;

    case METH_VARARGS:
    case METH_VARARGS | METH_KEYWORDS:
    {
        /* Slow-path: create a temporary tuple */
        PyObject *tuple;

        if (!(flags & METH_KEYWORDS) && kwargs != NULL && PyDict_Size(kwargs) != 0) {
            PyErr_Format(PyExc_TypeError,
                         "%.200s() takes no keyword arguments",
                         func->m_ml->ml_name);
            return NULL;
        }

        tuple = _PyStack_AsTuple(args, nargs);
        if (tuple == NULL) {
            return NULL;
        }

        if (flags & METH_KEYWORDS) {
            result = (*(PyCFunctionWithKeywords)meth) (self, tuple, kwargs);
        }
        else {
            result = (*meth) (self, tuple);
        }
        Py_DECREF(tuple);
        break;
    }

    case METH_FASTCALL:
    {
        PyObject **stack;
        PyObject *kwnames;
        _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth;

        if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) {
            return NULL;
        }

        result = (*fastmeth) (self, stack, nargs, kwnames);
        if (stack != args) {
            PyMem_Free(stack);
        }
        Py_XDECREF(kwnames);
        break;
    }

    default:
        PyErr_SetString(PyExc_SystemError,
                        "Bad call flags in PyCFunction_Call. "
                        "METH_OLDARGS is no longer supported!");
        return NULL;
    }

    result = _Py_CheckFunctionResult(func_obj, result, NULL);

    return result;
}

PyObject *
PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
    PyThreadState *tstate = PyThreadState_GET();
    return tstate->interp->eval_frame(f, throwflag);
}

static PyObject*
_PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
                     PyObject *globals)
{
    PyFrameObject *f;
    PyThreadState *tstate = PyThreadState_GET();
    PyObject **fastlocals;
    Py_ssize_t i;
    PyObject *result;

    PCALL(PCALL_FASTER_FUNCTION);
    assert(globals != NULL);
    /* XXX Perhaps we should create a specialized
       PyFrame_New() that doesn't take locals, but does
       take builtins without sanity checking them.
       */
    assert(tstate != NULL);
    f = PyFrame_New(tstate, co, globals, NULL);
    if (f == NULL) {
        return NULL;
    }

    fastlocals = f->f_localsplus;

    //将参数依次赋值给 f->f_localsplus
    for (i = 0; i < nargs; i++) {
        Py_INCREF(*args);
        fastlocals[i] = *args++;
    }
    result = PyEval_EvalFrameEx(f,0);

    ++tstate->recursion_depth;
    Py_DECREF(f);
    --tstate->recursion_depth;

    return result;
}

#define PyFunction_GET_GLOBALS(func) \
    (((PyFunctionObject *)func) -> func_globals)

#define PyFunction_GET_DEFAULTS(func) \
    (((PyFunctionObject *)func) -> func_defaults)

#define PyFunction_GET_KW_DEFAULTS(func) \
    (((PyFunctionObject *)func) -> func_kwdefaults)
#define PyFunction_GET_CLOSURE(func) \
    (((PyFunctionObject *)func) -> func_closure)
#define PyFunction_GET_ANNOTATIONS(func) \
    (((PyFunctionObject *)func) -> func_annotations)

static PyObject *
fast_function(PyObject *func, PyObject **stack,
              Py_ssize_t nargs, PyObject *kwnames)
{
    PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
    PyObject *globals = PyFunction_GET_GLOBALS(func);
    PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
    PyObject *kwdefs, *closure, *name, *qualname;
    PyObject **d;
    //kwnames 为扩展的位置参数
    Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
    Py_ssize_t nd;

    assert(PyFunction_Check(func));
    assert(nargs >= 0);
    assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
    assert((nargs == 0 && nkwargs == 0) || stack != NULL);
    /* kwnames must only contains str strings, no subclass, and all keys must
       be unique */

    PCALL(PCALL_FUNCTION);
    PCALL(PCALL_FAST_FUNCTION);

    if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
        co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
    {   //函数不含有扩展参数的情况
        if (argdefs == NULL && co->co_argcount == nargs) {
            return _PyFunction_FastCall(co, stack, nargs, globals);
        }
        //全都是默认参数
        else if (nargs == 0 && argdefs != NULL
                 && co->co_argcount == Py_SIZE(argdefs)) {
            /* function called with no arguments, but all parameters have
               a default value: use default values as arguments .*/
            stack = &PyTuple_GET_ITEM(argdefs, 0);
            return _PyFunction_FastCall(co, stack, Py_SIZE(argdefs), globals);
        }
    }

    kwdefs = PyFunction_GET_KW_DEFAULTS(func);
    closure = PyFunction_GET_CLOSURE(func);
    name = ((PyFunctionObject *)func) -> func_name;
    qualname = ((PyFunctionObject *)func) -> func_qualname;

    if (argdefs != NULL) {
        d = &PyTuple_GET_ITEM(argdefs, 0);
        nd = Py_SIZE(argdefs);
    }
    else {
        d = NULL;
        nd = 0;
    }
    return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
                                    stack, nargs,
                                    nkwargs ? &PyTuple_GET_ITEM(kwnames, 0) : NULL,
                                    stack + nargs,
                                    nkwargs, 1,
                                    d, (int)nd, kwdefs,
                                    closure, name, qualname);
}

#define GETLOCAL(i)     (fastlocals[i])

#define SETLOCAL(i, value)      do { PyObject *tmp = GETLOCAL(i); \
                                     GETLOCAL(i) = value; \
                                     Py_XDECREF(tmp); } while (0)

static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
           PyObject **args, Py_ssize_t argcount,
           PyObject **kwnames, PyObject **kwargs,
           Py_ssize_t kwcount, int kwstep,
           PyObject **defs, Py_ssize_t defcount,
           PyObject *kwdefs, PyObject *closure,
           PyObject *name, PyObject *qualname)
{
    PyCodeObject* co = (PyCodeObject*)_co;
    PyFrameObject *f;
    PyObject *retval = NULL;
    PyObject **fastlocals, **freevars;
    PyThreadState *tstate;
    PyObject *x, *u;
    const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
    Py_ssize_t i, n;
    PyObject *kwdict;

    if (globals == NULL) {
        PyErr_SetString(PyExc_SystemError,
                        "PyEval_EvalCodeEx: NULL globals");
        return NULL;
    }

    /* Create the frame */
    tstate = PyThreadState_GET();
    assert(tstate != NULL);
    f = PyFrame_New(tstate, co, globals, locals);
    if (f == NULL) {
        return NULL;
    }
    fastlocals = f->f_localsplus;
    freevars = f->f_localsplus + co->co_nlocals;

    /* Create a dictionary for keyword parameters (**kwags) */
    //扩展键值对参数
    if (co->co_flags & CO_VARKEYWORDS) {
        kwdict = PyDict_New();
        if (kwdict == NULL)
            goto fail;
        i = total_args;
        if (co->co_flags & CO_VARARGS) {
            i++;
        }
        SETLOCAL(i, kwdict);
    }
    else {
        kwdict = NULL;
    }

    /* Copy positional arguments into local variables */
    //扩展位置参数
    if (argcount > co->co_argcount) {
        n = co->co_argcount;
    }
    else {
        n = argcount;
    }
    for (i = 0; i < n; i++) {
        x = args[i];
        Py_INCREF(x);
        SETLOCAL(i, x);
    }

    /* Pack other positional arguments into the *args argument */
    if (co->co_flags & CO_VARARGS) {
        u = PyTuple_New(argcount - n);
        if (u == NULL) {
            goto fail;
        }
        SETLOCAL(total_args, u);
        for (i = n; i < argcount; i++) {
            x = args[i];
            Py_INCREF(x);
            PyTuple_SET_ITEM(u, i-n, x);
        }
    }

    /* Handle keyword arguments passed as two strided arrays */
    kwcount *= kwstep;
    for (i = 0; i < kwcount; i += kwstep) {
        PyObject **co_varnames;
        PyObject *keyword = kwnames[i];
        PyObject *value = kwargs[i];
        Py_ssize_t j;

        if (keyword == NULL || !PyUnicode_Check(keyword)) {
            PyErr_Format(PyExc_TypeError,
                         "%U() keywords must be strings",
                         co->co_name);
            goto fail;
        }

        /* Speed hack: do raw pointer compares. As names are
           normally interned this should almost always hit. */
        co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
        for (j = 0; j < total_args; j++) {
            PyObject *name = co_varnames[j];
            if (name == keyword) {
                goto kw_found;
            }
        }

        /* Slow fallback, just in case */
        for (j = 0; j < total_args; j++) {
            PyObject *name = co_varnames[j];
            int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ);
            if (cmp > 0) {
                goto kw_found;
            }
            else if (cmp < 0) {
                goto fail;
            }
        }

        if (j >= total_args && kwdict == NULL) {
            PyErr_Format(PyExc_TypeError,
                         "%U() got an unexpected keyword argument '%S'",
                         co->co_name, keyword);
            goto fail;
        }

        if (PyDict_SetItem(kwdict, keyword, value) == -1) {
            goto fail;
        }
        continue;

      kw_found:
        if (GETLOCAL(j) != NULL) {
            PyErr_Format(PyExc_TypeError,
                         "%U() got multiple values for argument '%S'",
                         co->co_name, keyword);
            goto fail;
        }
        Py_INCREF(value);
        SETLOCAL(j, value);
    }

    /* Check the number of positional arguments */
    if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) {
        too_many_positional(co, argcount, defcount, fastlocals);
        goto fail;
    }

    /* Add missing positional arguments (copy default values from defs) */
    if (argcount < co->co_argcount) {
        Py_ssize_t m = co->co_argcount - defcount;
        Py_ssize_t missing = 0;
        for (i = argcount; i < m; i++) {
            if (GETLOCAL(i) == NULL) {
                missing++;
            }
        }
        if (missing) {
            missing_arguments(co, missing, defcount, fastlocals);
            goto fail;
        }
        if (n > m)
            i = n - m;
        else
            i = 0;
        for (; i < defcount; i++) {
            if (GETLOCAL(m+i) == NULL) {
                PyObject *def = defs[i];
                Py_INCREF(def);
                SETLOCAL(m+i, def);
            }
        }
    }

    /* Add missing keyword arguments (copy default values from kwdefs) */
    if (co->co_kwonlyargcount > 0) {
        Py_ssize_t missing = 0;
        for (i = co->co_argcount; i < total_args; i++) {
            PyObject *name;
            if (GETLOCAL(i) != NULL)
                continue;
            name = PyTuple_GET_ITEM(co->co_varnames, i);
            if (kwdefs != NULL) {
                PyObject *def = PyDict_GetItem(kwdefs, name);
                if (def) {
                    Py_INCREF(def);
                    SETLOCAL(i, def);
                    continue;
                }
            }
            missing++;
        }
        if (missing) {
            missing_arguments(co, missing, -1, fastlocals);
            goto fail;
        }
    }

    /* Allocate and initialize storage for cell vars, and copy free
       vars into frame. */
    for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
        PyObject *c;
        int arg;
        /* Possibly account for the cell variable being an argument. */
        if (co->co_cell2arg != NULL &&
            (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) {
            c = PyCell_New(GETLOCAL(arg));
            /* Clear the local copy. */
            SETLOCAL(arg, NULL);
        }
        else {
            c = PyCell_New(NULL);
        }
        if (c == NULL)
            goto fail;
        //cellvars 在 locals 变量之后
        SETLOCAL(co->co_nlocals + i, c);
    }

    /* Copy closure variables to free variables */
    for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
        PyObject *o = PyTuple_GET_ITEM(closure, i);
        Py_INCREF(o);
        freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
    }

    /* Handle generator/coroutine/asynchronous generator */
    if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
        PyObject *gen;
        PyObject *coro_wrapper = tstate->coroutine_wrapper;
        int is_coro = co->co_flags & CO_COROUTINE;

        if (is_coro && tstate->in_coroutine_wrapper) {
            assert(coro_wrapper != NULL);
            PyErr_Format(PyExc_RuntimeError,
                         "coroutine wrapper %.200R attempted "
                         "to recursively wrap %.200R",
                         coro_wrapper,
                         co);
            goto fail;
        }

        /* Don't need to keep the reference to f_back, it will be set
         * when the generator is resumed. */
        Py_CLEAR(f->f_back);

        PCALL(PCALL_GENERATOR);

        /* Create a new generator that owns the ready to run frame
         * and return that as the value. */
        if (is_coro) {
            gen = PyCoro_New(f, name, qualname);
        } else if (co->co_flags & CO_ASYNC_GENERATOR) {
            gen = PyAsyncGen_New(f, name, qualname);
        } else {
            gen = PyGen_NewWithQualName(f, name, qualname);
        }
        if (gen == NULL)
            return NULL;

        if (is_coro && coro_wrapper != NULL) {
            PyObject *wrapped;
            tstate->in_coroutine_wrapper = 1;
            wrapped = PyObject_CallFunction(coro_wrapper, "N", gen);
            tstate->in_coroutine_wrapper = 0;
            return wrapped;
        }

        return gen;
    }

    retval = PyEval_EvalFrameEx(f,0);

fail: /* Jump here from prelude on failure */

    /* decref'ing the frame can cause __del__ methods to get invoked,
       which can call back into Python.  While we're done with the
       current Python frame (f), the associated C stack is still in use,
       so recursion_depth must be boosted for the duration.
    */
    assert(tstate != NULL);
    ++tstate->recursion_depth;
    Py_DECREF(f);
    --tstate->recursion_depth;
    return retval;
}

PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
            PyObject *locals)
{
    PyFrameObject *back = tstate->frame;
    PyFrameObject *f;
    PyObject *builtins;
    Py_ssize_t i;

    if (back == NULL || back->f_globals != globals) {
        builtins = _PyDict_GetItemId(globals, &PyId___builtins__);
        if (builtins) {
            if (PyModule_Check(builtins)) {
                builtins = PyModule_GetDict(builtins);
                assert(builtins != NULL);
            }
        }
        if (builtins == NULL) {
            /* No builtins!              Make up a minimal one
               Give them 'None', at least. */
            builtins = PyDict_New();
            if (builtins == NULL ||
                PyDict_SetItemString(
                    builtins, "None", Py_None) < 0)
                return NULL;
        }
        else
            Py_INCREF(builtins);

    }
    else {
        /* If we share the globals, we share the builtins.
           Save a lookup and a call. */
        builtins = back->f_builtins;
        assert(builtins != NULL);
        Py_INCREF(builtins);
    }
    if (code->co_zombieframe != NULL) {
        f = code->co_zombieframe;
        code->co_zombieframe = NULL;
        _Py_NewReference((PyObject *)f);
        assert(f->f_code == code);
    }
    else {
        Py_ssize_t extras, ncells, nfrees;
        ncells = PyTuple_GET_SIZE(code->co_cellvars);
        nfrees = PyTuple_GET_SIZE(code->co_freevars);
        //代码段的各个变量要在新建的 PyFrameObject 中保留
        extras = code->co_stacksize + code->co_nlocals + ncells +
            nfrees;
        if (free_list == NULL) {
            f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type,
            extras);
            if (f == NULL) {
                Py_DECREF(builtins);
                return NULL;
            }
        }
        else {
            assert(numfree > 0);
            --numfree;
            f = free_list;
            free_list = free_list->f_back;
            if (Py_SIZE(f) < extras) {
                PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
                if (new_f == NULL) {
                    PyObject_GC_Del(f);
                    Py_DECREF(builtins);
                    return NULL;
                }
                f = new_f;
            }
            _Py_NewReference((PyObject *)f);
        }

        f->f_code = code;
        //将调用者的 PyCodeObject 的 co_nlocals n_cells, nfrees 赋值给 f_localsplus
        extras = code->co_nlocals + ncells + nfrees; //没有包含  code->stacksize
        f->f_valuestack = f->f_localsplus + extras;
        for (i=0; i<extras; i++)
            f->f_localsplus[i] = NULL;
        f->f_locals = NULL;
        f->f_trace = NULL;
        f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL;
    }
    f->f_stacktop = f->f_valuestack;
    f->f_builtins = builtins;
    Py_XINCREF(back);
    f->f_back = back;
    Py_INCREF(code);
    Py_INCREF(globals);
    f->f_globals = globals;
    /* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */
    if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) ==
        (CO_NEWLOCALS | CO_OPTIMIZED))
        ; /* f_locals = NULL; will be set by PyFrame_FastToLocals() */
    else if (code->co_flags & CO_NEWLOCALS) {
        locals = PyDict_New();
        if (locals == NULL) {
            Py_DECREF(f);
            return NULL;
        }
        f->f_locals = locals;
    }
    else {
    //用全局变量初始化局部变量
        if (locals == NULL)
            locals = globals;
        Py_INCREF(locals);
        f->f_locals = locals;
    }

    f->f_lasti = -1;
    f->f_lineno = code->co_firstlineno;
    f->f_iblock = 0;
    f->f_executing = 0;
    f->f_gen = NULL;

    _PyObject_GC_TRACK(f);
    return f;
}

对于 C 函数,调用 (func->m_ml -> ml_meth)(args) 参数,这里参数包括没有参数,
一个参数,可变参数

对于 Python 本身的函数调用 PyEval_EvalFrameEx,对应无参数的进行了优化,有参数的
进行各种解析。

至此,我们应该对 Python 的执行机制有了更加深刻的理解。大体描述如下:
编译的时候,每个函数会编译为一个单独的 PyCodeObject;
运行的是时候首先,整个文件是一个 PyFrameObject 对象,每个 PyFrameObject 对象包含
PyCodeObject,在 PyCodeObject 执行过程中遇到函数调用,会创建一个新的 PyFunctionObject 对象,
并将编译的 PyCodeObject 赋值给该对象,根据函数类型判断是 C 函数还是 Python
本身的函数。如果是 C 函数, 对调用 (func->m_ml -> ml_meth)(args),如果是 Python
函数,会创建一个新的 PyFrameObject,并用 PyFunctionObject 初始化该 PyFrameObject
对象,最后执行 PyFunctionObject 中 PyCodeObject 中的字节码。

此外,还有一个细节需要注意的是 f_globals,虽然所有函数共享全局变量,但是,
事实上每个函数访问全局变量的时候,并不是依次向 上搜索,而是在调用的是,
直接将父函数的 globals 传递给子函数。这样子函数直接搜索自身的 f_globals
就可以了。这样虽然占用了内存,但是提高了效率。

扫描二维码关注公众号,回复: 4619675 查看本文章

两个问题:
1. 递归是怎么实现的?
2. 前面定义的函数可以调用后面定义的函数么? 为什么?

参数

python 中参数包括

  1. 位置参数,如 f(a, b)
  2. 键值对参数,如 f(a = 1, b = 2)
  3. 扩展位置参数,如 f(a, b, *argv)
  4. 扩展键值对参数,如 f(a, b, **argv)

例如:

#!/usr/bin/env python
# encoding: utf-8

def test():
    pass

def test1(a):
    pass

def test2(a, b):
    pass

def test3(a, b, *argv):
    pass

def test4(a, b, **argv):
    pass

test()
test1(1)
test2(1, 2)
test3(1, 2, 3)
test3(1, 2, 3, 4)
test4(1, 2, c = 3, d = 4)

编译之后

magic b'330d0d0a'
moddate b'8788345b' (Thu Jun 28 15:04:39 2018)
files sz 803
code
   argcount 0
   nlocals 0
   stacksize 6
   flags 0040
   code
      b'6400640184005a006402640384005a016404640584005a02640664078400'
      b'5a036408640984005a046500830001006501640a830101006502640a640b'
      b'830201006503640a640b640c830301006503640a640b640c640d83040100'
      b'6504640a640b640c640d640e8d040100640f5300'
  4           0 LOAD_CONST               0 (<code object test at 0x103828ae0, file "/tmp/a.py", line 4>)
              2 LOAD_CONST               1 ('test')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (test)

  7           8 LOAD_CONST               2 (<code object test1 at 0x103839ae0, file "/tmp/a.py", line 7>)
             10 LOAD_CONST               3 ('test1')
             12 MAKE_FUNCTION            0
             14 STORE_NAME               1 (test1)

 10          16 LOAD_CONST               4 (<code object test2 at 0x103839f60, file "/tmp/a.py", line 10>)
             18 LOAD_CONST               5 ('test2')
             20 MAKE_FUNCTION            0
             22 STORE_NAME               2 (test2)

 13          24 LOAD_CONST               6 (<code object test3 at 0x103837b70, file "/tmp/a.py", line 13>)
             26 LOAD_CONST               7 ('test3')
             28 MAKE_FUNCTION            0
             30 STORE_NAME               3 (test3)

 16          32 LOAD_CONST               8 (<code object test4 at 0x1038375d0, file "/tmp/a.py", line 16>)
             34 LOAD_CONST               9 ('test4')
             36 MAKE_FUNCTION            0
             38 STORE_NAME               4 (test4)

 19          40 LOAD_NAME                0 (test)
             42 CALL_FUNCTION            0               //没参数,直接调用
             44 POP_TOP

 20          46 LOAD_NAME                1 (test1)
             48 LOAD_CONST              10 (1)
             50 CALL_FUNCTION            1               //一个参数
             52 POP_TOP

 21          54 LOAD_NAME                2 (test2)
             56 LOAD_CONST              10 (1)
             58 LOAD_CONST              11 (2)
             60 CALL_FUNCTION            2               //两个参数
             62 POP_TOP

 22          64 LOAD_NAME                3 (test3)
             66 LOAD_CONST              10 (1)
             68 LOAD_CONST              11 (2)
             70 LOAD_CONST              12 (3)
             72 CALL_FUNCTION            3               //三个参数
             74 POP_TOP

 23          76 LOAD_NAME                3 (test3)
             78 LOAD_CONST              10 (1)
             80 LOAD_CONST              11 (2)
             82 LOAD_CONST              12 (3)
             84 LOAD_CONST              13 (4)          //四个参数
             86 CALL_FUNCTION            4
             88 POP_TOP

 24          90 LOAD_NAME                4 (test4)
             92 LOAD_CONST              10 (1)
             94 LOAD_CONST              11 (2)
             96 LOAD_CONST              12 (3)
             98 LOAD_CONST              13 (4)
            100 LOAD_CONST              14 (('c', 'd')) //键值对
            102 CALL_FUNCTION_KW         4              //四个参数
            104 POP_TOP
            106 LOAD_CONST              15 (None)
            108 RETURN_VALUE
   consts
      code
         argcount 0
         nlocals 0                      //没有参数,没有局部变量
         stacksize 1
         flags 0043
         code b'64005300'
  5           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
         consts
            None
         names ()
         varnames ()
         freevars ()
         cellvars ()
         filename '/tmp/a.py'
         name 'test'
         firstlineno 4
         lnotab b'0001'
      'test'
      code
         argcount 1
         nlocals 1                      //一个参数,一个局部变量
         stacksize 1
         flags 0043
         code b'64005300'
  8           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
         consts
            None
         names ()
         varnames ('a',)
         freevars ()
         cellvars ()
         filename '/tmp/a.py'
         name 'test1'
         firstlineno 7
         lnotab b'0001'
      'test1'
      code
         argcount 2
         nlocals 2                       //两个参数,两个局部变量
         stacksize 1
         flags 0043
         code b'64005300'
 11           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
         consts
            None
         names ()
         varnames ('a', 'b')
         freevars ()
         cellvars ()
         filename '/tmp/a.py'
         name 'test2'
         firstlineno 10
         lnotab b'0001'
      'test2'
      code
         argcount 2
         nlocals 3                       //两个参数,三个局部变量,其中一个为 argv
         stacksize 1
         flags 0047
         code b'64005300'
 14           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
         consts
            None
         names ()
         varnames ('a', 'b', 'argv')
         freevars ()
         cellvars ()
         filename '/tmp/a.py'
         name 'test3'
         firstlineno 13
         lnotab b'0001'
      'test3'
      code
         argcount 2
         nlocals 3                       //两个参数,三个局部变量,其中一个为 argv
         stacksize 1
         flags 004b
         code b'64005300'
 17           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
         consts
            None
         names ()
         varnames ('a', 'b', 'argv')
         freevars ()
         cellvars ()
         filename '/tmp/a.py'
         name 'test4'
         firstlineno 16
         lnotab b'0001'
      'test4'
      1
      2
      3
      4
      ('c', 'd')
      None
   names ('test', 'test1', 'test2', 'test3', 'test4')
   varnames ()
   freevars ()
   cellvars ()
   filename '/tmp/a.py'
   name '<module>'
   firstlineno 4
   lnotab b'08030803080308030803060108010a010c010e01'

当包含位置参数的时候,位置参数会保存在局部变量中,之后传递给函数。你可以试着编译如下代码来验证自己的想法。

def test5(a, b, *args, **argv):
    pass
test5(1, 2, 3, 4, c = 5, d = 6)

函数参数从左到右依次压入栈中,才实际调用的时候,会创建
PyFrameObject,这些参数会传递给 PyFrameObject 的 f_localsplus,
当访问函数参数的时候,直接通过索引定位到 f_localsplus 中的参数,
这也是位置参数的由来。

函数分类

从 MAKE_FUNCTION 中可以看出,python 中包括 5 中函数

  • func->func_closure
  • func->func_annotations
  • func->func_kwdefaults
  • func->func_defaults
  • 以上都为空

而且以上五中并不是互斥的,他们可任意组合。

TARGET(CALL_FUNCTION_KW) {
    PyObject **sp, *res, *names;

    names = POP();
    assert(PyTuple_CheckExact(names) && PyTuple_GET_SIZE(names) <= oparg);
    PCALL(PCALL_ALL);
    sp = stack_pointer;
    res = call_function(&sp, oparg, names);
    stack_pointer = sp;
    PUSH(res);
    Py_DECREF(names);

    if (res == NULL) {
        goto error;
    }
    DISPATCH();
}

TARGET(CALL_FUNCTION_EX) {
    PyObject *func, *callargs, *kwargs = NULL, *result;
    PCALL(PCALL_ALL);
    if (oparg & 0x01) {
        kwargs = POP();
        if (!PyDict_CheckExact(kwargs)) {
            PyObject *d = PyDict_New();
            if (d == NULL)
                goto error;
            if (PyDict_Update(d, kwargs) != 0) {
                Py_DECREF(d);
                /* PyDict_Update raises attribute
                 * error (percolated from an attempt
                 * to get 'keys' attribute) instead of
                 * a type error if its second argument
                 * is not a mapping.
                 */
                if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
                    format_kwargs_mapping_error(SECOND(), kwargs);
                }
                Py_DECREF(kwargs);
                goto error;
            }
            Py_DECREF(kwargs);
            kwargs = d;
        }
        assert(PyDict_CheckExact(kwargs));
    }
    callargs = POP();
    func = TOP();
    if (!PyTuple_CheckExact(callargs)) {
        if (check_args_iterable(func, callargs) < 0) {
            Py_DECREF(callargs);
            goto error;
        }
        Py_SETREF(callargs, PySequence_Tuple(callargs));
        if (callargs == NULL) {
            goto error;
        }
    }
    assert(PyTuple_CheckExact(callargs));

    result = do_call_core(func, callargs, kwargs);
    Py_DECREF(func);
    Py_DECREF(callargs);
    Py_XDECREF(kwargs);

    SET_TOP(result);
    if (result == NULL) {
        goto error;
    }
    DISPATCH();
}

static PyObject *
do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict)
{
    if (PyCFunction_Check(func)) {
        PyObject *result;
        PyThreadState *tstate = PyThreadState_GET();
        C_TRACE(result, PyCFunction_Call(func, callargs, kwdict));
        return result;
    }
    else {
        return PyObject_Call(func, callargs, kwdict);
    }
}

PyObject *
PyObject_Call(PyObject *func, PyObject *args, PyObject *kwargs)
{
    ternaryfunc call;
    PyObject *result;

    /* PyObject_Call() must not be called with an exception set,
       because it may clear it (directly or indirectly) and so the
       caller loses its exception */
    assert(!PyErr_Occurred());
    assert(PyTuple_Check(args));
    assert(kwargs == NULL || PyDict_Check(kwargs));

    call = func->ob_type->tp_call;
    if (call == NULL) {
        PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
                     func->ob_type->tp_name);
        return NULL;
    }

    if (Py_EnterRecursiveCall(" while calling a Python object"))
        return NULL;

    result = (*call)(func, args, kwargs);

    Py_LeaveRecursiveCall();

    return _Py_CheckFunctionResult(func, result, NULL);
}

局部变量

#!/usr/bin/env python
# encoding: utf-8

def test1(a):
    a = 1
    b = 2
    c = 3

test1(3)

闭包与装饰器

#!/usr/bin/env python
# encoding: utf-8

def cmp_closure(base):
    v = 19
    def cmp_to(value):
        if value > 20:
            value = v
        return base < value

    return cmp_to

def cmp_noclosure(base):
    v = 19
    def cmp_to(value, base = base):
        if value > 20:
            value = v
        return base < value

    return cmp_to

cmp1 = cmp_closure(10)
print(cmp1(9))
print(cmp1(11))

cmp1 = cmp_noclosure(10)
print(cmp1(9))
print(cmp1(11))
print(cmp1(12, 13))
magic b'330d0d0a'
moddate b'e4a6345b' (Thu Jun 28 17:14:12 2018)
files sz 1678
code
   argcount 0
   nlocals 0
   stacksize 4
   flags 0040
   code
      b'6400640184005a006402640384005a016500640483015a02650365026405'
      b'8301830101006503650264068301830101006501640483015a0265036502'
      b'640583018301010065036502640683018301010065036502640764088302'
      b'8301010064095300'
  4           0 LOAD_CONST               0 (<code object cmp_closure at 0x103072c00, file "/tmp/a.py", line 4>)
              2 LOAD_CONST               1 ('cmp_closure')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (cmp_closure)

 13           8 LOAD_CONST               2 (<code object cmp_noclosure at 0x103083ae0, file "/tmp/a.py", line 13>)
             10 LOAD_CONST               3 ('cmp_noclosure')
             12 MAKE_FUNCTION            0
             14 STORE_NAME               1 (cmp_noclosure)

 22          16 LOAD_NAME                0 (cmp_closure)
             18 LOAD_CONST               4 (10)
             20 CALL_FUNCTION            1
             22 STORE_NAME               2 (cmp1)

 23          24 LOAD_NAME                3 (print)
             26 LOAD_NAME                2 (cmp1)
             28 LOAD_CONST               5 (9)
             30 CALL_FUNCTION            1
             32 CALL_FUNCTION            1
             34 POP_TOP

 24          36 LOAD_NAME                3 (print)
             38 LOAD_NAME                2 (cmp1)
             40 LOAD_CONST               6 (11)
             42 CALL_FUNCTION            1
             44 CALL_FUNCTION            1
             46 POP_TOP

 26          48 LOAD_NAME                1 (cmp_noclosure)
             50 LOAD_CONST               4 (10)
             52 CALL_FUNCTION            1
             54 STORE_NAME               2 (cmp1)

 27          56 LOAD_NAME                3 (print)
             58 LOAD_NAME                2 (cmp1)
             60 LOAD_CONST               5 (9)
             62 CALL_FUNCTION            1
             64 CALL_FUNCTION            1
             66 POP_TOP

 28          68 LOAD_NAME                3 (print)
             70 LOAD_NAME                2 (cmp1)
             72 LOAD_CONST               6 (11)
             74 CALL_FUNCTION            1
             76 CALL_FUNCTION            1
             78 POP_TOP

 29          80 LOAD_NAME                3 (print)
             82 LOAD_NAME                2 (cmp1)
             84 LOAD_CONST               7 (12)
             86 LOAD_CONST               8 (13)
             88 CALL_FUNCTION            2
             90 CALL_FUNCTION            1
             92 POP_TOP
             94 LOAD_CONST               9 (None)
             96 RETURN_VALUE
   consts
      code
         argcount 1
         nlocals 2
         stacksize 3
         flags 0003
         code b'640189018700870166026402640384087d017c015300'
  5           0 LOAD_CONST               1 (19)
              2 STORE_DEREF              1 (v)

  6           4 LOAD_CLOSURE             0 (base)
              6 LOAD_CLOSURE             1 (v)
              8 BUILD_TUPLE              2
             10 LOAD_CONST               2 (<code object cmp_to at 0x103072ae0, file "/tmp/a.py", line 6>)
             12 LOAD_CONST               3 ('cmp_closure.<locals>.cmp_to')
             14 MAKE_FUNCTION            8
             16 STORE_FAST               1 (cmp_to)

 11          18 LOAD_FAST                1 (cmp_to)
             20 RETURN_VALUE
         consts
            None
            19
            code
               argcount 1
               nlocals 1
               stacksize 2
               flags 0013
               code b'7c0064016b04720c88017d0088007c006b005300'
  7           0 LOAD_FAST                0 (value)
              2 LOAD_CONST               1 (20)
              4 COMPARE_OP               4 (>)
              6 POP_JUMP_IF_FALSE       12

  8           8 LOAD_DEREF               1 (v)
             10 STORE_FAST               0 (value)

  9     >>   12 LOAD_DEREF               0 (base)
             14 LOAD_FAST                0 (value)
             16 COMPARE_OP               0 (<)
             18 RETURN_VALUE
               consts
                  None
                  20
               names ()
               varnames ('value',)
               freevars ('base', 'v')
               cellvars ()
               filename '/tmp/a.py'
               name 'cmp_to'
               firstlineno 6
               lnotab b'000108010401'
            'cmp_closure.<locals>.cmp_to'
         names ()
         varnames ('base', 'cmp_to')
         freevars ()
         cellvars ('base', 'v')
         filename '/tmp/a.py'
         name 'cmp_closure'
         firstlineno 4
         lnotab b'000104010e05'
      'cmp_closure'
      code
         argcount 1
         nlocals 2
         stacksize 4
         flags 0003
         code b'640189007c006601870066016402640384097d017c015300'
 14           0 LOAD_CONST               1 (19)
              2 STORE_DEREF              0 (v)

 15           4 LOAD_FAST                0 (base)
              6 BUILD_TUPLE              1
              8 LOAD_CLOSURE             0 (v)
             10 BUILD_TUPLE              1
             12 LOAD_CONST               2 (<code object cmp_to at 0x103083f60, file "/tmp/a.py", line 15>)
             14 LOAD_CONST               3 ('cmp_noclosure.<locals>.cmp_to')
             16 MAKE_FUNCTION            9 //这里会初始化 func_closure, func_defaults
             18 STORE_FAST               1 (cmp_to)

 20          20 LOAD_FAST                1 (cmp_to)
             22 RETURN_VALUE
         consts
            None
            19
            code
               argcount 2
               nlocals 2
               stacksize 2
               flags 0013
               code b'7c0064016b04720c88007d007c017c006b005300'
 16           0 LOAD_FAST                0 (value)
              2 LOAD_CONST               1 (20)
              4 COMPARE_OP               4 (>)
              6 POP_JUMP_IF_FALSE       12

 17           8 LOAD_DEREF               0 (v)
             10 STORE_FAST               0 (value)

 18     >>   12 LOAD_FAST                1 (base)
             14 LOAD_FAST                0 (value)
             16 COMPARE_OP               0 (<)
             18 RETURN_VALUE
               consts
                  None
                  20
               names ()
               varnames ('value', 'base')
               freevars ('v',)
               cellvars ()
               filename '/tmp/a.py'
               name 'cmp_to'
               firstlineno 15
               lnotab b'000108010401'
            'cmp_noclosure.<locals>.cmp_to'
         names ()
         varnames ('base', 'cmp_to')
         freevars ()
         cellvars ('v',)
         filename '/tmp/a.py'
         name 'cmp_noclosure'
         firstlineno 13
         lnotab b'000104011005'
      'cmp_noclosure'
      10
      9
      11
      12
      13
      None
   names ('cmp_closure', 'cmp_noclosure', 'cmp1', 'print')
   varnames ()
   freevars ()
   cellvars ()
   filename '/tmp/a.py'
   name '<module>'
   firstlineno 4
   lnotab b'0809080908010c010c0208010c010c01'

字节码可以看出,
1. 调用 cmp_closure ,返回一个函数对象
2. 调用 cmp1 指向的函数

符合 Lazy 加载模式


TARGET(LOAD_DEREF) {
    PyObject *cell = freevars[oparg];
    PyObject *value = PyCell_GET(cell);
    if (value == NULL) {
        format_exc_unbound(co, oparg);
        goto error;
    }
    Py_INCREF(value);
    PUSH(value);
    DISPATCH();
}
TARGET(STORE_DEREF) {
    PyObject *v = POP();
    PyObject *cell = freevars[oparg];
    PyObject *oldobj = PyCell_GET(cell);
    PyCell_SET(cell, v);
    Py_XDECREF(oldobj);
    DISPATCH();
}

PyObject *
PyCell_New(PyObject *obj)
{
    PyCellObject *op;

    op = (PyCellObject *)PyObject_GC_New(PyCellObject, &PyCell_Type);
    if (op == NULL)
        return NULL;
    op->ob_ref = obj;
    Py_XINCREF(obj);

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

PyObject *
PyCell_Get(PyObject *op)
{
    if (!PyCell_Check(op)) {
        PyErr_BadInternalCall();
        return NULL;
    }
    Py_XINCREF(((PyCellObject*)op)->ob_ref);
    return PyCell_GET(op);
}

int
PyCell_Set(PyObject *op, PyObject *obj)
{
    PyObject* oldobj;
    if (!PyCell_Check(op)) {
        PyErr_BadInternalCall();
        return -1;
    }
    oldobj = PyCell_GET(op);
    Py_XINCREF(obj);
    PyCell_SET(op, obj);
    Py_XDECREF(oldobj);
    return 0;
}

TARGET(LOAD_FAST) {
    PyObject *value = GETLOCAL(oparg);
    if (value == NULL) {
        format_exc_check_arg(PyExc_UnboundLocalError,
                             UNBOUNDLOCAL_ERROR_MSG,
                             PyTuple_GetItem(co->co_varnames, oparg));
        goto error;
    }
    Py_INCREF(value);
    PUSH(value);
    FAST_DISPATCH();
}
        TARGET(BUILD_TUPLE) {
            PyObject *tup = PyTuple_New(oparg);
            if (tup == NULL)
                goto error;
            while (--oparg >= 0) {
                PyObject *item = POP();
                PyTuple_SET_ITEM(tup, oparg, item);
            }
            PUSH(tup);
            DISPATCH();
        }

        TARGET(LOAD_CLOSURE) {
            PyObject *cell = freevars[oparg];
            Py_INCREF(cell);
            PUSH(cell);
            DISPATCH();
        }

        TARGET(STORE_FAST) {
            PyObject *value = POP();
            SETLOCAL(oparg, value);
            FAST_DISPATCH();
        }

生成器

TODO

猜你喜欢

转载自blog.csdn.net/wenxueliu/article/details/80920026
今日推荐