28

为什么在 C 中返回 Py_None 之前需要 Py_INCREF(Py_None),如下所示?

Py_INCREF(Py_None);
return Py_None;

如果 Py_INCREF(Py_None) 被省略,会发生什么?

4

2 回答 2

27

缺少 aPy_INCREF将导致对 的引用计数不正确Py_None,这可能导致解释器解除分配Py_None。由于是在文件Py_None中静态分配的:Objects/object.c

PyObject _Py_NoneStruct = {
  _PyObject_EXTRA_INIT
  1, &PyNone_Type
};

里面有Include/object.h定义:

#define Py_None (&_Py_NoneStruct)

那么会发生什么,解释器将因致命错误而崩溃:

Fatal Python error: deallocating None

由以下none_dealloc函数生成Objects/object.c

/* ARGUSED */
static void
none_dealloc(PyObject* ignore)
{
    /* This should never get called, but we also don't want to SEGV if
     * we accidentally decref None out of existence.
     */
    Py_FatalError("deallocating None");
}

正如该评论所述,如果NoneType没有自己的解除分配功能,您将获得分段错误,因为free将在堆栈上完成调用。

您可以复制教程Py_DECREF(Py_None)中的示例,在函数中添加调用Noddy_name,构建扩展并执行循环调用该方法。


在一般情况下,引用计数0会导致程序以许多不同的方式失败。

特别是 python 可以自由地重用被释放的对象使用的内存,这意味着突然对对象的每个引用都可以成为对随机对象(或空内存位置)的引用,你可以看到类似:

>>> None   #or whatever object that was deallocated
<ARandomObjectYouNeverSawBefore object at ...>

(这实际上有时发生在我身上,在编写 C 扩展时。由于缺少对 的调用,一些对象在随机时间变成只读缓冲区Py_INCREF)。

在其他情况下,可能会引发不同类型的错误,或者解释器可能会崩溃或段错误。

于 2013-03-08T06:38:58.470 回答
25

Py_None实际上只是另一个 Python 对象,除了没有方法。

Python 将计算对任何PyObject*. 它是字符串、整数还是 None 都没有关系。

如果不增加引用计数,Python 解释器最终会在引用计数达到 后丢弃该对象0,认为没有任何指向该对象的指针。这意味着下次您尝试对返回值做某事时,您将跟随一个指向内存中不能保证保存的位置的指针Py_None(错误、奇怪的值、分段错误等)。

除了必须记住使用之外,还有其他选择Py_INCREF(Py_None)

return Py_BuildValue("");

或者

Py_RETURN_NONE;
于 2013-03-08T06:48:12.917 回答