PyDict_SetItem是否增加了键的引用计数,如果是,则在代码中的哪个位置进行?

问题描述 投票:1回答:1

TLDR:PyDict_SetItem增加键和值,但是这在代码中的什么地方发生?

[PyDict_SetItem呼叫insertdict

[insertdict]立即对键和值执行Py_INCREF。但是,在end of the success path处,它会在键上执行Py_DECREF(但不会在值上)。在执行此PY_INCREF之前,它在键上执行了额外的PY_DECREF时,我必须缺少该代码的某些部分。 我的问题是,这些额外的PY_INCREF在何处发生,为什么在哪里发生?为什么在Py_INCREF开头的初始insertdict不够?

由此,乍看之下PyDict_SetItem仅增加了值的引用计数,而不增加了键。当然,这是不正确的。例如,在使用PyDict_SetItemStringchar *中,通过PyUnicode_FromString将其转换为PyObject(返回一个新值),并在调用Py_DECREF之后对该新值执行PyDict_SetItem。如果PyDict_SetItem不增加密钥,而PyDict_SetItemString减少刚刚创建的密钥,则程序最终可能会出现段错误。鉴于这种情况不会发生,似乎我在这里缺少了一些东西。


最后,此代码应证明PyDict_SetItem会同时增加键和值,并且调用者应同时取消键和值,除非借用了它们的引用/或将键和值提供给其他人。

#include <Python.h>
#include <stdio.h>

int main(void)
{
    Py_Initialize();
    PyObject *dict = NULL, *key = NULL, *value = NULL;
    int i = 5000;
    char *o = "foo";

    if (!(dict = PyDict_New())) {
        goto error;
    }
    if (!(key = Py_BuildValue("i", i))) {
        goto error;
    }
    if (!(value = Py_BuildValue("s", o))) {
        goto error;
    }
    printf("Before PyDict_SetItem\n");
    printf("key is %i\n", key->ob_refcnt);  /* Prints 1 */
    printf("value is %i\n", value->ob_refcnt);  /* Prints 1 */

    printf("Calling PyDict_SetItem\n");
    if (PyDict_SetItem(dict, key, value) < 0) {
        goto error;
    }
    printf("key is %i\n", key->ob_refcnt);  /* Prints 2 */
    printf("value is %i\n", value->ob_refcnt);  /* Prints 2 */

    printf("Decrefing key and value\n");
    Py_DECREF(key);
    Py_DECREF(value);
    printf("key is %i\n", key->ob_refcnt);   /* Prints 1 */
    printf("value is %i\n", value->ob_refcnt);   /* Prints 1 */

    Py_Finalize();
    return 0; // would return the dict in normal code
error:
    printf("error");
    Py_XDECREF(dict);
    Py_XDECREF(key);
    Py_XDECREF(value);
    Py_Finalize();
    return 1;
}

您可以像这样编译:

gcc -c -I/path/to/python/include/python3.7m dict.c
gcc dict.o -L/path/to/python/lib/python3.7/config-3.7m-i386-linux-gnu -L/path/to/python/lib -Wl,-rpath=/path/to/python/lib -lpython3.7m -lpthread -lm -ldl -lutil -lrt -Xlinker -export-dynamic -m32
python python-3.x python-c-api
1个回答
0
投票

Py_DECREF(key);并非所有成功都发生。它发生在成功更新现有字典条目而不是创建新密钥时,因此不会插入提供的密钥。在新输入路径上,insertdict命中了另一个return语句,并且不对键进行解引用。

© www.soinside.com 2019 - 2024. All rights reserved.