__init__ 中的成员分配和参考计数
Member assignment and ref counting in __init__
我正在为 Python 编写 C 扩展并完成 documentation,我很难理解 __init__
函数中的成员分配。
因此,在第 2.2 节中,成员分配如下:
if (first) {
tmp = self->first;
Py_INCREF(first);
self->first = first;
Py_XDECREF(tmp);
}
后面解释说:
Our type doesn’t restrict the type of the first member, so it could be any kind of object. It could have a destructor that causes code to be executed that tries to access the first member; or that destructor could release the Global interpreter Lock and let arbitrary code run in other threads that accesses and modifies our object.
如果我Py_XDECREF
它,self->first
就会失效。
我理解这样的情况,如果析构函数释放全局解释器锁,就会有一个悬空指针,我的对象 (self
) 可能会被修改,等等......一切都会失控。
但为什么析构函数访问它会出现问题?为什么这部分:
It could have a destructor that causes code to be executed that tries to access the first member;
有问题吗?如果 self->first
的析构函数访问自身,没问题。我不在乎,这是它的问题。
希望我说得够清楚了。感谢您的回复。
每当您允许任意 Python 代码到 运行 时,您需要确保您的对象处于有效状态。
当你 DECREF
一个对象时,任意 Python 代码可能是 运行,例如,如果被删除的对象有一个 __del__
方法(或者如果它是一个 C 扩展方法 a tp_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_INCREF(first);
self->first = first;
当时 T.__del__
会 运行(由 Py_XDECREF
触发),它会访问 c.first
,那时候它是一个无效对象(因为它有0
).
的引用计数
在此示例中,意外地没有中断(在我的计算机上),因为内存尚未重新使用。但是,如果它稍微复杂一些,它 通常会终止 Python 进程(在我的计算机上):
c = Custom("Firstname", "Lastname", 10)
class T:
def __del__(self):
print("T", c.first)
class U:
def __init__(self, t):
self.t = t
def __del__(self):
print("U")
c.first = U(T())
c.__init__(U(T())) # repeated multiple times to make the crash more likely
c.__init__(U(T()))
c.__init__(U(T()))
通过在调用DECREF
之前确保对象处于有效状态(不仅仅是在[=26期间),可以避免(并且应该避免)所有这些=] 但无处不在!),或者将其设置为 null
:
Py_CLEAR(self->first); // Sets the field to null before XDECREF is used
Py_INCREF(first);
self->first = first;
或者立即将其替换为 first
:
tmp = self->first;
Py_INCREF(first);
self->first = first;
Py_XDECREF(tmp);
或者如果你不想重复使用这种成语,你也可以创建一个这样的函数:
static void replace_field(PyObject **field, PyObject *val)
{
PyObject *tmp = *field;
*field = val;
Py_XDECREF(tmp);
}
将代码简化为:
Py_INCREF(first);
replace_field(&self->first, first);
我正在为 Python 编写 C 扩展并完成 documentation,我很难理解 __init__
函数中的成员分配。
因此,在第 2.2 节中,成员分配如下:
if (first) {
tmp = self->first;
Py_INCREF(first);
self->first = first;
Py_XDECREF(tmp);
}
后面解释说:
Our type doesn’t restrict the type of the first member, so it could be any kind of object. It could have a destructor that causes code to be executed that tries to access the first member; or that destructor could release the Global interpreter Lock and let arbitrary code run in other threads that accesses and modifies our object.
如果我Py_XDECREF
它,self->first
就会失效。
我理解这样的情况,如果析构函数释放全局解释器锁,就会有一个悬空指针,我的对象 (self
) 可能会被修改,等等......一切都会失控。
但为什么析构函数访问它会出现问题?为什么这部分:
It could have a destructor that causes code to be executed that tries to access the first member;
有问题吗?如果 self->first
的析构函数访问自身,没问题。我不在乎,这是它的问题。
希望我说得够清楚了。感谢您的回复。
每当您允许任意 Python 代码到 运行 时,您需要确保您的对象处于有效状态。
当你 DECREF
一个对象时,任意 Python 代码可能是 运行,例如,如果被删除的对象有一个 __del__
方法(或者如果它是一个 C 扩展方法 a tp_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_INCREF(first);
self->first = first;
当时 T.__del__
会 运行(由 Py_XDECREF
触发),它会访问 c.first
,那时候它是一个无效对象(因为它有0
).
在此示例中,意外地没有中断(在我的计算机上),因为内存尚未重新使用。但是,如果它稍微复杂一些,它 通常会终止 Python 进程(在我的计算机上):
c = Custom("Firstname", "Lastname", 10)
class T:
def __del__(self):
print("T", c.first)
class U:
def __init__(self, t):
self.t = t
def __del__(self):
print("U")
c.first = U(T())
c.__init__(U(T())) # repeated multiple times to make the crash more likely
c.__init__(U(T()))
c.__init__(U(T()))
通过在调用DECREF
之前确保对象处于有效状态(不仅仅是在[=26期间),可以避免(并且应该避免)所有这些=] 但无处不在!),或者将其设置为 null
:
Py_CLEAR(self->first); // Sets the field to null before XDECREF is used
Py_INCREF(first);
self->first = first;
或者立即将其替换为 first
:
tmp = self->first;
Py_INCREF(first);
self->first = first;
Py_XDECREF(tmp);
或者如果你不想重复使用这种成语,你也可以创建一个这样的函数:
static void replace_field(PyObject **field, PyObject *val)
{
PyObject *tmp = *field;
*field = val;
Py_XDECREF(tmp);
}
将代码简化为:
Py_INCREF(first);
replace_field(&self->first, first);