TLDR:PyDict_SetItem
增加键和值,但是这在代码中的什么地方发生?
PyDict_SetItem调用insertdict
。
insertdict立即Py_INCREF
对键和值执行操作。但是,在成功路径的末尾,它将Py_DECREF
在键上执行a (但不执行值)。在执行此操作之前,该代码中一定有一部分是我所缺少的,它PY_INCREF
在键上做了额外的工作PY_DECREF
。我的问题是,此额外的PY_INCREF在何处发生,为什么会发生?为什么最初Py_INCREF
的开头insertdict
不足?
由此看来,乍看之下PyDict_SetItem
只会增加值的引用计数,而不会增加键。当然,这不是真的。例如,在PyDict_SetItemString中,使用a char *
,将其通过转换为PyObject PyUnicode_FromString
(返回一个新值),并Py_DECREF
在调用后对该新值执行a 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
该Py_DECREF(key);
中insertdict
不会发生的所有成就。如果已经存在一个相等的密钥,这是成功发生的原因,要么是因为存在一个现有条目,要么是因为该dict是一个拆分表dict,它与拥有该密钥的其他dict共享密钥。在该路径上,没有插入提供的密钥,因此Py_INCREF(key);
需要取消原始密钥。
在不存在键的路径上,insertdict
命中一条不同的return
语句,并且不对键进行解引用:
if (ix == DKIX_EMPTY) {
/* Insert into new slot. */
assert(old_value == NULL);
if (mp->ma_keys->dk_usable <= 0) {
/* Need to resize. */
if (insertion_resize(mp) < 0)
goto Fail;
}
Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
ep->me_key = key;
ep->me_hash = hash;
if (mp->ma_values) {
assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL);
mp->ma_values[mp->ma_keys->dk_nentries] = value;
}
else {
ep->me_value = value;
}
mp->ma_used++;
mp->ma_version_tag = DICT_NEXT_VERSION();
mp->ma_keys->dk_usable--;
mp->ma_keys->dk_nentries++;
assert(mp->ma_keys->dk_usable >= 0);
ASSERT_CONSISTENT(mp);
return 0;
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句