Python 初始化中的成员赋值和引用计数__
我正在为Python编写一个C扩展,并且正在经历,我很难理解Python 初始化中的成员赋值和引用计数__,python,python-c-api,Python,Python C Api,我正在为Python编写一个C扩展,并且正在经历,我很难理解\uuu init\uu函数中的成员赋值 因此,在第2.2节中,成员分配如下: if (first) { tmp = self->first; Py_INCREF(first); self->first = first; Py_XDECREF(tmp); } 后来的解释是: 我们的类型不限制第一个成员的类型,因此它可以是任何类型的对象。它可以有一个析构函数,使试图访问第一个成员的代码被执行;
\uuu init\uu
函数中的成员赋值
因此,在第2.2节中,成员分配如下:
if (first) {
tmp = self->first;
Py_INCREF(first);
self->first = first;
Py_XDECREF(tmp);
}
后来的解释是:
我们的类型不限制第一个成员的类型,因此它可以是任何类型的对象。它可以有一个析构函数,使试图访问第一个成员的代码被执行;或者,析构函数可以释放全局解释器锁,让任意代码在访问和修改对象的其他线程中运行
如果IPy\u XDECREF
it,self->first
将变得无效
我理解这种情况,如果析构函数释放全局解释器锁,就会有一个悬空指针,我的对象(self
)可能会被修改,等等。。。一切都失去了控制
但是为什么析构函数访问它是一个问题呢?为什么这一部分:
它可以有一个析构函数,使试图访问第一个成员的代码被执行
这是一个问题吗?如果self->first
的析构函数访问自身,则可以。我不在乎,这是它的问题
希望我足够清楚。感谢您的回复。无论何时允许运行任意Python代码,都需要确保对象处于有效状态 当您
DECREF
一个对象时,可能会运行任意Python代码,例如,如果要删除的对象有一个\uuuu del\uuuu
方法(或者如果它是一个C扩展方法atp\u dealloc
)。该代码可以执行任何操作,例如(如引用的文本中所述)访问实例的first
属性
例如:
c = Custom("Firstname", "Lastname", 10)
class T:
def __del__(self):
print("T", c.first) # access the first attribute of the "c" variable
c.first = T()
c.__init__(T())
c.__init__(T())
c.__init__(T())
现在,如果您的C代码如下所示:
Py_XDECREF(self->first);
Py_增量(第一);
自我->第一=第一;
当T.\uuu del_uu
运行时(由Py\uxdecref
触发),它将首先访问c.first
,此时它是一个无效对象(因为它的引用计数为0
)
在本例中,由于内存尚未重新使用,因此它不会意外地(在我的计算机上)中断。但是,如果稍微复杂一点,它通常会终止Python进程(在我的计算机上):
c=Custom(“Firstname”,“Lastname”,10)
T类:
定义(自我):
打印(“T”,c.优先)
U类:
定义初始化(self,t):
self.t=t
定义(自我):
打印(“U”)
c、 第一个=U(T())
c、 _uuinit_uu(T())#重复多次以增加崩溃的可能性
c、 _uuuinit_uuu(T()))
c、 _uuuinit_uuu(T()))
通过在调用
DECREF
之前确保对象处于有效状态(不仅仅是在\uuuuu init\uuuuu
期间,而且是在任何地方!),或者将其设置为null
,可以避免(也应该避免)所有这些情况:
Py_CLEAR(self->first);//在使用XDECREF之前,将字段设置为null
Py_增量(第一);
自我->第一=第一;
或者立即将其替换为第一个:
tmp=self->first;
Py_增量(第一);
自我->第一=第一;
Py_XDECREF(tmp);
或者,如果您不想重复使用这种习惯用法,也可以创建如下函数:
static void replace_字段(PyObject**字段,PyObject*val)
{
PyObject*tmp=*字段;
*字段=val;
Py_XDECREF(tmp);
}
这将代码简化为:
Py_INCREF(第一);
替换_字段(&self->first,first);
不太清楚混淆在哪里,但可能在这里:首先引用的对象可能有一个对正在初始化的对象的引用,如果代码不小心,另一个对象的析构函数本身可能会首先更改并导致问题。可以写一个更好的描述作为一个答案,如果这是正确的…没有考虑回参考问题。谢谢你指出@SamMason然而,在这种情况下,first
的破坏者到底为什么要用back-reference(self->first
的self
)做些什么呢?因为我总是注意我在\uu del_u
中所做的事情,所以我没有想到这种(邪恶的)可能性。谢谢你的Py_CLEAR
宏,起初在文档中没有看到它