Skip to content

Latest commit

 

History

History
622 lines (512 loc) · 20.2 KB

函数.md

File metadata and controls

622 lines (512 loc) · 20.2 KB

函数和方法(method)

PyFunctionObject:

typedef struct {
    PyObject_HEAD
    PyObject *func_code;        /* A code object, the __code__ attribute */
    PyObject *func_globals;     /* A dictionary (other mappings won't do) */
    // func_defaults 保存函数参数的默认值, 例如 def func(a, b, c=1, d=2)
    PyObject *func_defaults;    /* NULL or a tuple */
    // func_kwdefaults 保存函数参数的默认值, 不过和 func_defaults 不同, 当参数中定义了 *args 是才会使用 func_kwdefaults
    // 例如 def func(a, b, *args, c=1, d=2)
    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_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 */
    vectorcallfunc vectorcall;

    /* 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;

每次遇到 def 语句时, 就是创建一个 PyFunctionObject 对象.

创建函数对象

case 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();
    }
    /* def func(a, b, *args, c = 1, d = 2, **kwargs):
        LOAD_CONST              14 (1)
        LOAD_CONST              15 (2)
        LOAD_CONST              16 (('c', 'd'))
        BUILD_CONST_KEY_MAP      2
        LOAD_CONST              17 (<code object>)
        LOAD_CONST              18 ('func')
        MAKE_FUNCTION            2 (kwdefaults)
    */
    if (oparg & 0x02) {
        assert(PyDict_CheckExact(TOP()));
        func->func_kwdefaults = POP();
    }
    /* def func(a, b, c='c', d='d')
        LOAD_CONST              29 (('c', 'd'))
        LOAD_CONST               6 (<code object>)
        LOAD_CONST               7 ('func')
        MAKE_FUNCTION            1 (defaults)
    */
    if (oparg & 0x01) {
        assert(PyTuple_CheckExact(TOP()));
        func->func_defaults = POP();
    }

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

MAKE_FUNCTION 的参数 oparg 表示函数的形参是否有默认值, 以及函数是否有类型注解.

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

    /* __module__: If module name is in globals, use it.
       Otherwise, use None. */
    module = PyDict_GetItemWithError(globals, __name__);
    if (module) {
        Py_INCREF(module);
    }
    else if (PyErr_Occurred()) {
        return NULL;
    }

    op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);
    if (op == NULL) {
        Py_XDECREF(module);
        return NULL;
    }
    /* Note: No failures from this point on, since func_dealloc() does not
       expect a partially-created object. */

    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;
    op->vectorcall = _PyFunction_Vectorcall;
    op->func_module = module;

    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_annotations = NULL;

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

调用函数

// Python/ceval.c

case TARGET(CALL_FUNCTION): {
    PREDICTED(CALL_FUNCTION);
    PyObject **sp, *res;
    sp = stack_pointer;
    res = call_function(tstate, &sp, oparg, NULL);
    stack_pointer = sp;
    PUSH(res);
    if (res == NULL) {
        goto error;
    }
    DISPATCH();
}

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

    names = POP();
    assert(PyTuple_Check(names));
    assert(PyTuple_GET_SIZE(names) <= oparg);
    /* We assume without checking that names contains only strings */
    sp = stack_pointer;
    res = call_function(tstate, &sp, oparg, names);
    stack_pointer = sp;
    PUSH(res);
    Py_DECREF(names);

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

case TARGET(CALL_FUNCTION_EX): {
    PREDICTED(CALL_FUNCTION_EX);
    PyObject *func, *callargs, *kwargs = NULL, *result;
    if (oparg & 0x01) {
        kwargs = POP();
        if (!PyDict_CheckExact(kwargs)) {
            PyObject *d = PyDict_New();
            if (d == NULL)
                goto error;
            if (_PyDict_MergeEx(d, kwargs, 2) < 0) {
                Py_DECREF(d);
                format_kwargs_error(tstate, 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(tstate, 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(tstate, func, callargs, kwargs);
    Py_DECREF(func);
    Py_DECREF(callargs);
    Py_XDECREF(kwargs);

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

从上面的代码可以看出, 有三个调用函数的字节码, 分别是 CALL_FUNCTION, CALL_FUNCTION_KWCALL_FUNCTION_EX.

CALL_FUNCTIONCALL_FUNCTION_KW 会调用 call_function 进行函数调用, 而 CALL_FUNCTION_EX 会调用 do_call_core 进行函数.

CALL_FUNCTION 用于实参只有位置参数的情况, 例如 f(1, 2, 3).

CALL_FUNCTION_KW 用于实参包括位置参数和关键字参数的情况, 例如 f(1, 2, a=3, b=4), f(a=3, b=4).

CALL_FUNCTION_EX 用于实参是可变参数的情况, 例如 f(*[1, 2], **{'a': 3, 'b': 4})

call_function

// Python/ceval.c

Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION
call_function(PyThreadState *tstate, 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);
    Py_ssize_t nargs = oparg - nkwargs;
    PyObject **stack = (*pp_stack) - nargs - nkwargs;

    if (tstate->use_tracing) {
        x = trace_call_function(tstate, func, stack, nargs, kwnames);
    }
    else {
        x = PyObject_Vectorcall(func, stack, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
    }

    assert((x != NULL) ^ (_PyErr_Occurred(tstate) != NULL));

    /* Clear the stack of the function object. */
    while ((*pp_stack) > pfunc) {
        w = EXT_POP(*pp_stack);
        Py_DECREF(w);
    }

    return x;
}

call_function 最终会调用 _PyObject_VectorcallTstate, 而 _PyObject_VectorcallTstate 最终会调用 callable 对象中的 vectorcallfunc 函数.

/* Call the callable object 'callable' with the "vectorcall" calling
   convention.

   args is a C array for positional arguments.

   nargsf is the number of positional arguments plus optionally the flag
   PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to
   modify args[-1].

   kwnames is a tuple of keyword names. The values of the keyword arguments
   are stored in "args" after the positional arguments (note that the number
   of keyword arguments does not change nargsf). kwnames can also be NULL if
   there are no keyword arguments.

   keywords must only contain strings and all keys must be unique.

   Return the result on success. Raise an exception and return NULL on
   error. */
static inline PyObject *
_PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
                           PyObject *const *args, size_t nargsf,
                           PyObject *kwnames)
{
    vectorcallfunc func;
    PyObject *res;

    assert(kwnames == NULL || PyTuple_Check(kwnames));
    assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);

    func = PyVectorcall_Function(callable);
    if (func == NULL) {
        Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
        return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames);
    }
    res = func(callable, args, nargsf, kwnames);
    return _Py_CheckFunctionResult(tstate, callable, res, NULL);
}

关于 vectorcall 的信息可以参考 PEP 590 -- Vectorcall: a fast calling protocol for CPython.

vectorcall 看来简化了早期 tp_call 函数调用约定的复杂过程, 上面的 PEP 590 说 vectorcall 一定程度减少了分配对象的次数, 理论上性能有所提升, 但是实际上并不会带来明显可见的性能提升. 主要还是简化函数调用的流程.

支持 vectorcall 的 callable 对象的类型需要在 tp_flags 中设置 Py_TPFLAGS_HAVE_VECTORCALL 标志位, 并且 callable 对象要设置一个被调用的函数, 该函数的原型是

typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args,
                                    size_t nargsf, PyObject *kwnames);

普通函数对象 PyFunctionObject 的 vectorcallfunc 设置为 _PyFunction_Vectorcall:

PyObject *
PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
{
    op->vectorcall = _PyFunction_Vectorcall;
}

_PyFunction_Vectorcall:

// Objects/call.c
PyObject *
_PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
                       size_t nargsf, PyObject *kwnames)
{
    assert(PyFunction_Check(func));
    assert(kwnames == NULL || PyTuple_CheckExact(kwnames));

    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
    assert(nargs >= 0);
    Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
    assert((nargs == 0 && nkwargs == 0) || stack != NULL);
    /* kwnames must only contain strings and all keys must be unique */

    PyThreadState *tstate = _PyThreadState_GET();
    PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
    PyObject *globals = PyFunction_GET_GLOBALS(func);
    PyObject *argdefs = PyFunction_GET_DEFAULTS(func);

    if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
        (co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
    {
        // CO_NOFREE 说明函数不是嵌套函数.
        // nkwargs == 0 说明实参没有关键字参数

        // 函数的形参没有默认值, 实参都是位置参数(即没有关键字参数), 那么进行快速调用.
        if (argdefs == NULL && co->co_argcount == nargs) {
            return function_code_fastcall(tstate, co, stack, nargs, globals);
        }
        // 没有实参, 而且函数的所有形参都有默认值
        else if (nargs == 0 && argdefs != NULL
                 && co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
            /* function called with no arguments, but all parameters have
               a default value: use default values as arguments .*/
            stack = _PyTuple_ITEMS(argdefs);
            return function_code_fastcall(tstate, co,
                                          stack, PyTuple_GET_SIZE(argdefs),
                                          globals);
        }
    }

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

    PyObject **d;
    Py_ssize_t nd;
    if (argdefs != NULL) {
        d = _PyTuple_ITEMS(argdefs);
        nd = PyTuple_GET_SIZE(argdefs);
        assert(nd <= INT_MAX);
    }
    else {
        d = NULL;
        nd = 0;
    }
    return _PyEval_EvalCode(tstate,
                (PyObject*)co, globals, (PyObject *)NULL,
                stack, nargs,
                nkwargs ? _PyTuple_ITEMS(kwnames) : NULL,
                stack + nargs,
                nkwargs, 1,
                d, (int)nd, kwdefs,
                closure, name, qualname);
}

_PyFunction_Vectorcall 有两个选择, 一是调用 function_code_fastcall 执行函数, 二是调用 _PyEval_EvalCode 执行函数. _PyEval_EvalCode 的代码非常复杂, 后面再讲.

function_code_fastcall 负责简单的函数调用.

_PyEval_EvalCode 负责复杂的函数调用, 例如闭包函数, 实参和形参比较复杂的函数调用.

do_call_core

// Python/ceval.c

static PyObject *
do_call_core(PyThreadState *tstate, PyObject *func, PyObject *callargs, PyObject *kwdict)
{
    PyObject *result;

    if (PyCFunction_CheckExact(func) || PyCMethod_CheckExact(func)) {
        C_TRACE(result, PyObject_Call(func, callargs, kwdict));
        return result;
    }
    else if (Py_IS_TYPE(func, &PyMethodDescr_Type)) {
        Py_ssize_t nargs = PyTuple_GET_SIZE(callargs);
        if (nargs > 0 && tstate->use_tracing) {
            /* We need to create a temporary bound method as argument
               for profiling.

               If nargs == 0, then this cannot work because we have no
               "self". In any case, the call itself would raise
               TypeError (foo needs an argument), so we just skip
               profiling. */
            PyObject *self = PyTuple_GET_ITEM(callargs, 0);
            func = Py_TYPE(func)->tp_descr_get(func, self, (PyObject*)Py_TYPE(self));
            if (func == NULL) {
                return NULL;
            }

            C_TRACE(result, _PyObject_FastCallDictTstate(
                                    tstate, func,
                                    &_PyTuple_ITEMS(callargs)[1],
                                    nargs - 1,
                                    kwdict));
            Py_DECREF(func);
            return result;
        }
    }
    return PyObject_Call(func, callargs, kwdict);
}

do_call_core 最终调用 PyObject_Call, 而 PyObject_Call 一般最终会调用 callable 对象所属类型的 tp_call.

tp_call

几个实例

_PyEval_EvalCode

_PyEval_EvalCode 的大部分工作都是在处理各种实参和形参的情况, 并将实参放入 f->f_localsplus(fastlocals) 数组. 具体的细节可以查看代码.

闭包

code 对象

闭包函数的 code 对象的 co_flags 中包含 CO_NESTED(0x10).

创建函数对象时保存闭包变量

例子:

def f():
    a = 1
    def g():
        b = a + 1
    g()

字节码如下:

Disassembly of <code object f at 0x7f8dbc96aa80, file "mydemo/function_closure_demo.py", line 1>:
  2           0 LOAD_CONST               1 (1)
              2 STORE_DEREF              0 (a)

  3           4 LOAD_CLOSURE             0 (a)
              6 BUILD_TUPLE              1
              8 LOAD_CONST               2 (<code object g at 0x7f8dbc96a9d0, file "mydemo/function_closure_demo.py", line 3>)
             10 LOAD_CONST               3 ('f.<locals>.g')
             12 MAKE_FUNCTION            8 (closure)
             14 STORE_FAST               0 (g)

  5          16 LOAD_FAST                0 (g)
             18 CALL_FUNCTION            0
             20 POP_TOP
             22 LOAD_CONST               0 (None)
             24 RETURN_VALUE

Disassembly of <code object g at 0x7f8dbc96a9d0, file "mydemo/function_closure_demo.py", line 3>:
  4           0 LOAD_DEREF               0 (a)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 STORE_FAST               0 (b)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

STORE_DEREF 将值保存到当前 frame 的 cellvars 中, cellvars 中的元素类型是 PyCellObject, 该值被包裹在 PyCellObject 中.

case TARGET(STORE_DEREF): {
    PyObject *v = POP();
    PyObject *cell = freevars[oparg];
    PyObject *oldobj = PyCell_GET(cell);
    PyCell_SET(cell, v);
    Py_XDECREF(oldobj);
    DISPATCH();
}

LOAD_CLOSUREMAKE_FUNCTION 对应的处理器如下:

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

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

可见 LOAD_CLOSURE 是直接从当前 frame 的 freevars 数组读取值, 速度相较于 LOAD_NAME 更快. 注意: LOAD_CLOSURE 是从当前 frame 的 cellvals 中读取, 获取的值是 PyCellObject 类型.

func ->func_closure 保存了内层函数引用外层函数的变量, 类型是 tuple.

在调用函数时, func->func_closure 保存到 f->f_localplus 中, 代码如下:

PyObject *
_PyEval_EvalCode(PyThreadState *tstate,
           PyObject *_co, PyObject *globals, PyObject *locals,
           PyObject *const *args, Py_ssize_t argcount,
           PyObject *const *kwnames, PyObject *const *kwargs,
           Py_ssize_t kwcount, int kwstep,
           PyObject *const *defs, Py_ssize_t defcount,
           PyObject *kwdefs, PyObject *closure,
           PyObject *name, PyObject *qualname)
{
    /* 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;
    }
}

在内层函数 g 中, 使用 LOAD_DEREF 读取引用自外层函数作用域的变量.

case TARGET(LOAD_DEREF): {
    PyObject *cell = freevars[oparg];
    PyObject *value = PyCell_GET(cell);
    if (value == NULL) {
        format_exc_unbound(tstate, co, oparg);
        goto error;
    }
    Py_INCREF(value);
    PUSH(value);
    DISPATCH();
}

总结

函数调用在 CPython 内部的实现非常复杂, 难以用简单的语言来概括, 想要理清内部的各种调用链还是要仔细看代码.

这里的函数调用不仅仅指常规意义的形如 func(a, b, c) 的函数调用, 在 Python 的世界里, 只要是可调用的对象都可以当成函数来调用, 其内部实现也类似常规函数的调用, 从字节码来看都是使用 CALL_FUNCTION. 例如, 类的实例化 MyClass(a, b, c) 其实就是 CALL_FUNCTIOON, 甚至类的定义 class MyClass: 也是通过 MAKE_FUNCTIONCALL_FUNCTION 实现的.

从总体的角度来看, 函数调用的难点在于处理复杂的实参和形参, 如果参数简单, 那么 CPython 内部会选择快速调用的方法. 当处理好复杂的参数后, 创建一个新的 PyFrameObject, 参数加载进 PyFrameObject 的 fastlocals, 最后开始执行 PyFrameObject 的字节码.

对于解释器来说, 它只负责执行 PyFrameObject. 因此, PyFunctionObject 只是中间对象, 最终会被转换成 PyFrameObject.