为什么在C中返回Py_None之前需要Py_INCREF(Py_None),如下所示?
Py_INCREF(Py_None);
return Py_None;
如果省略Py_INCREF(Py_None),会发生什么?
答案 0 :(得分:23)
缺少Py_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
调用。
您可以在tutorial中测试复制示例,将Py_DECREF(Py_None)
的调用添加到Noddy_name
函数中,构建扩展并执行调用该方法的循环。
在一般情况下,引用计数0
会导致程序以多种不同方式失败。
特别是python可以自由地重用已被解除分配的对象使用的内存,这意味着突然对对象的每个引用都可以成为对随机对象(或空内存位置)的引用,你可以看看像:
>>> None #or whatever object that was deallocated
<ARandomObjectYouNeverSawBefore object at ...>
(实际上happened对我来说,当编写C扩展时。由于缺少对Py_INCREF
的调用而在随机时间变成只读缓冲区的一些对象。
在其他情况下,可能会出现不同类型的错误,或者解释程序可能崩溃或发生段错误。
答案 1 :(得分:19)
Py_None
实际上只是另一个Python对象,除非没有方法。
Python会计算对任何PyObject*
的引用。如果它是字符串,整数或无,则无关紧要。
如果你没有增加引用计数,Python解释器最终会在引用计数达到0
之后丢弃该对象,认为没有任何指向该对象的指针。这意味着下次尝试使用返回值执行某些操作时,您将跟随指向内存中无法保证保留Py_None
的位置的指针(错误,奇怪值,分段错误等)
还有其他方法可以记住使用Py_INCREF(Py_None)
:
return Py_BuildValue("");
或
Py_RETURN_NONE;