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_KW
和 CALL_FUNCTION_EX
.
CALL_FUNCTION
和 CALL_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})
// 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 负责复杂的函数调用, 例如闭包函数, 实参和形参比较复杂的函数调用.
// 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.
_PyEval_EvalCode 的大部分工作都是在处理各种实参和形参的情况, 并将实参放入 f->f_localsplus
(fastlocals) 数组. 具体的细节可以查看代码.
闭包函数的 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_CLOSURE
和 MAKE_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_FUNCTION
和 CALL_FUNCTION
实现的.
从总体的角度来看, 函数调用的难点在于处理复杂的实参和形参, 如果参数简单, 那么 CPython 内部会选择快速调用的方法. 当处理好复杂的参数后, 创建一个新的 PyFrameObject, 参数加载进 PyFrameObject 的 fastlocals, 最后开始执行 PyFrameObject 的字节码.
对于解释器来说, 它只负责执行 PyFrameObject. 因此, PyFunctionObject 只是中间对象, 最终会被转换成 PyFrameObject.