TLDR: PyDict_SetItem
увеличивает ключ и значение, но где в коде это происходит?
PyDict_SetItem делает вызов insertdict
.
insertdict немедленно выполняет Py_INCREF
как для ключа, так и для значения. Однако в конце пути успеха он делает Py_DECREF
для ключа (но не для значения). Должна быть некоторая часть этого кода, которую я пропускаю, когда он делает дополнительный PY_INCREF
для ключа, прежде чем он сделает это PY_DECREF
. У меня вопрос, где и почему происходит этот дополнительный PY_INCREF? Почему начальный Py_INCREF
в начале insertdict
недостаточен?
Из этого на первый взгляд кажется, что PyDict_SetItem
только увеличивает счетчик ссылок значения, но не ключа. Это не правда, конечно. Например, в PyDict_SetItemString , который принимает char *
, преобразует его в PyObject через PyUnicode_FromString
(который возвращает новое значение), выполняет Py_DECREF
для этого нового значения после вызова PyDict_SetItem
, Если PyDict_SetItem
не увеличивает ключ, а PyDict_SetItemString
уменьшает ключ, который он только что создал, программа может в конечном итоге выполнить segfault. Учитывая, что этого не происходит, кажется, что я что-то здесь упускаю.
Наконец, этот код должен доказать, что 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