CPython 3.6中函数参数的引用计数

2024-09-30 06:25:03 发布

您现在位置:Python中文网/ 问答频道 /正文

为了找出传递给函数的参数是“临时”(只传递到函数中)还是在外部引用,我使用Py_REFCNT。这是在一个C扩展包中完成的,但是为了更容易再现,我决定在这里提供一个基于ipythonmagic的Cython实现。在

在cpython3.5和cpython3.6之间,接受多个参数的函数(对于只接受一个参数的函数,它仍能正常工作)似乎发生了一些变化:

In [1]: %load_ext cython

In [2]: %%cython
   ...: cdef extern from "Python.h":
   ...:     Py_ssize_t Py_REFCNT(object o)
   ...:
   ...: cpdef func(o, p):
   ...:     return Py_REFCNT(o)

当我在3.5上运行代码时,它会给出预期的结果:

^{pr2}$

但是在3.6版本中它给了我2

>>> import numpy as np
>>> func(np.ones(3), np.ones(3))
2

在评论中,有人问我关于C代码的问题,这里是:

static PyObject *
GetRefCount(PyObject *m, PyObject *args) {
    if (PyTuple_CheckExact(args) && PyTuple_Size(args) > 0) {
        Py_ssize_t reference_count = Py_REFCNT(PyTuple_GET_ITEM(args, 0));
        return PyLong_FromSsize_t(reference_count);
    }
    PyErr_SetString(PyExc_TypeError, "wrong input");
    return NULL;
}

方法定义:

    {"getrefcount",                                     /* ml_name */
     (PyCFunction)GetRefCount,                          /* ml_meth */
     METH_VARARGS,                                      /* ml_flags */
     ""                                                 /* ml_doc */
     },

结果相同:

>>> import numpy as np
>>> getrefcount(np.ones(3))  # 3.5
1
>>> getrefcount(np.ones(3))  # 3.6
2

我想知道在3.6中引用计数在哪里(以及为什么)增加。我浏览了CPython源代码/Python问题跟踪程序,但没有找到答案。在


Tags: 函数inpy参数returnnponesargs
1条回答
网友
1楼 · 发布于 2024-09-30 06:25:03

在Python3.5中,执行函数时,参数恰好从调用方堆栈中清除。在Python3.6中,参数碰巧仍然在调用方的堆栈中以及函数的参数元组中。在

在Python3.5上,函数调用通过here

    else {
        PyObject *callargs;
        callargs = load_args(pp_stack, na);
        if (callargs != NULL) {
            READ_TIMESTAMP(*pintr0);
            C_TRACE(x, PyCFunction_Call(func,callargs,NULL));
            READ_TIMESTAMP(*pintr1);
            Py_XDECREF(callargs);
        }
        else {
            x = NULL;
        }
    }

从堆栈中移除参数以构建参数元组:

^{pr2}$

在3.6中,函数调用通过here

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

经过here

^{4}$

经过here

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

    tuple = _PyStack_AsTuple(args, nargs);

    ...
}

经过here

for (i=0; i < nargs; i++) {
    PyObject *item = stack[i];
    Py_INCREF(item);
    PyTuple_SET_ITEM(args, i, item);
}

它将参数留在堆栈上,并使用对参数的新引用构建一个元组。在

相关问题 更多 >

    热门问题