Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 初始化中的成员赋值和引用计数___Python_Python C Api - Fatal编程技术网

Python 初始化中的成员赋值和引用计数__

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); } 后来的解释是: 我们的类型不限制第一个成员的类型,因此它可以是任何类型的对象。它可以有一个析构函数,使试图访问第一个成员的代码被执行;

我正在为Python编写一个C扩展,并且正在经历,我很难理解
\uuu init\uu
函数中的成员赋值

因此,在第2.2节中,成员分配如下:

if (first) {
    tmp = self->first;
    Py_INCREF(first);
    self->first = first;
    Py_XDECREF(tmp);
}
后来的解释是:

我们的类型不限制第一个成员的类型,因此它可以是任何类型的对象。它可以有一个析构函数,使试图访问第一个成员的代码被执行;或者,析构函数可以释放全局解释器锁,让任意代码在访问和修改对象的其他线程中运行

如果I
Py\u XDECREF
it,
self->first
将变得无效

我理解这种情况,如果析构函数释放全局解释器锁,就会有一个悬空指针,我的对象(
self
)可能会被修改,等等。。。一切都失去了控制

但是为什么析构函数访问它是一个问题呢?为什么这一部分:

它可以有一个析构函数,使试图访问第一个成员的代码被执行

这是一个问题吗?如果
self->first
的析构函数访问自身,则可以。我不在乎,这是它的问题


希望我足够清楚。感谢您的回复。

无论何时允许运行任意Python代码,都需要确保对象处于有效状态

当您
DECREF
一个对象时,可能会运行任意Python代码,例如,如果要删除的对象有一个
\uuuu del\uuuu
方法(或者如果它是一个C扩展方法a
tp\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
宏,起初在文档中没有看到它