为何自定义类型初始化器设置的属性需受保护?CPython实现疑问
为什么旧对象析构器访问
first成员会有风险? 问题出在错误版本代码顺序导致的对象生命周期漏洞,我们用具体场景拆解:
假设自定义类型实例self原本的first成员指向对象old_obj,而old_obj的析构器(Python中的__del__方法)存在访问self.first的逻辑——比如old_obj持有对self的引用,销毁时尝试读取self的first属性做清理操作。
错误版本的执行流程(风险点)
if (first) { Py_XDECREF(self->first); // 步骤1:old_obj引用计数减1,可能触发析构 Py_INCREF(first); self->first = first; // 步骤3:才将新值赋值给self->first }
- 步骤1执行后,若
old_obj的引用计数降至0,会立即触发其析构器。 - 此时
self->first仍指向正在被销毁的old_obj,析构器访问self.first时,拿到的是一个已进入销毁流程的无效对象——这会引发野指针访问、对象状态异常,甚至直接崩溃。 - 哪怕析构器只是间接依赖
self的状态,也可能在新值未就位的情况下触发逻辑错误。
正确版本的执行流程(避免风险)
if (first) { tmp = self->first; // 保存旧值 Py_INCREF(first); self->first = first; // 先将新值赋值到位 Py_XDECREF(tmp); // 再销毁旧值 }
- 先完成新值的引用计数递增和赋值,确保
self->first始终指向有效对象。 - 销毁旧值时,哪怕旧对象的析构器访问
self.first,拿到的已是新的、有效的first对象,不会出现访问无效对象的问题。
简言之,错误版本的顺序让self->first出现了一段“空窗期”(指向已销毁对象),而正确版本通过“先赋值新值,再清理旧值”的顺序,保证了self->first的有效性全程不受影响,从根源避免了析构器访问时的风险。
内容的提问来源于stack exchange,提问作者Carpetfizz
相关产品推荐
相关产品推荐

