1

我正在尝试让我的 c 扩展名的Py_INCREF's 和Py_DECREF's 正确。在这样做的同时,我一直在为发电机的真正高价值而磕磕绊绊。我一直在做的事情如下:

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

有人可以解释一下吗。

4

1 回答 1

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标识函数,除了增加引用计数并返回未修改的参数之外什么都不做),那么计数可能会更高如果其他地方可能存在对同一迭代器的其他引用。

假设它们不是为了简洁而被省略,您确实需要添加对返回值的检查;NULL如果引发异常,几乎所有 API 调用都可能返回,并且默默地继续处理而不是立即传递错误,可能会冒着用(无用的)段错误替换(有用的)异常消息/回溯的风险。

于 2019-12-16T18:45:09.377 回答