C扩展名:生成器的引用计数

时间:2019-12-16 17:06:13

标签: python python-3.x python-c-api refcounting

我正在尝试为我的c扩展名使用Py_INCREFPy_DECREF。 在这样做的同时,我一直在为发电机的高价值着迷。我有什么 正在做的事情如下:

// Forgive me for leaving out the NULL checks
PyObject *get_generator = PyUnicode_InternFromString("get_generator");
PyObject *callback;
PyObject *seq;
PyObject *item;

callback = PyObject_CallMethodObjArgs(parent->state, get_generator, NULL);
seq = PyObject_GetIter(tmp);

Py_DECREF(callback);

while ((item = PyIter_Next(seq))) {
    ...
    Code
    ...
    Py_DECREF(item);
}
Py_DECREF(seq);
printf("!!!%zd\n", seq);

有人可以解释吗。

1 个答案:

答案 0 :(得分:2)

printf("!!!%zd\n", seq);不在打印引用计数,而是在打印原始指针地址(由于不能保证指针和带符号的size_t可以兼容打印,因此略有错误)。

还请注意,如果您不持有自己拥有的指针(或已经安全借用了指针),则检查真正的引用计数是不安全的,因此在Py_DECREF之后(放弃所有权时)进行检查是不确定的行为。

要修复,请更改:

Py_DECREF(seq);
printf("!!!%zd\n", seq);

收件人:

printf("!!!%zd\n", Py_REFCNT(seq));
Py_DECREF(seq);

在正常情况下,您应该期望看到计数为1,尽管如果传递给PyObject_GetIter的对象本身是迭代器(使PyObject_GetIter为标识函数,除了增加引用计数和返回原本未修改的参数),则如果其他地方可能存在对同一迭代器的其他引用,则计数可能会更高。

假设不是简单起见,您确实需要添加对返回值的检查;如果引发异常,几乎所有的API调用都可能返回NULL,并且以静默方式继续处理而不是立即传递错误可能会用(无用的)segfault替换(有用的)异常消息/回溯。