В CPython У меня есть два типа объектов, которые тесно связаны друг с другом.Как разбить прямой ссылочный цикл в CPython
#include <Python.h>
#include <structmember.h>
typedef struct pyt PYT;
struct pyt { PyObject_HEAD PYT *other; };
static void dealloc (PYT *self) {
Py_CLEAR(self->other);
printf("dealloc object at %p\n", self);
PyObject_GC_Del(self);
}
static PyTypeObject Pyt2Type = {
PyObject_HEAD_INIT(NULL)
0, "pyt.Pyt2", sizeof(PYT), 0,
(destructor) dealloc
};
static PyObject * new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
PYT *self = PyObject_GC_New(PYT, type);
if (!self) return NULL;
self->other = PyObject_GC_New(PYT, &Pyt2Type);
if (!self->other) { Py_DECREF(self); return NULL; }
return Py_INCREF(self), self->other->other = self, (PyObject *) self;
}
static PyTypeObject Pyt1Type = {
PyObject_HEAD_INIT(NULL)
0, "pyt.Pyt1", sizeof(PYT), 0,
(destructor) dealloc
};
static int traverse (PYT *self, visitproc visit, void *arg) {
Py_VISIT(self->other);
return 0;
}
static int clear (PYT *self) {
Py_CLEAR(self->other);
return 0;
}
static PyMemberDef members[] = {
{"other", T_OBJECT, offsetof(PYT, other), RO, "other"},
{ NULL }
};
static PyMethodDef methods[] = {{ NULL }};
PyMODINIT_FUNC initpyt (void) {
PyObject* m;
Pyt1Type.tp_flags = Pyt2Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
Pyt1Type.tp_traverse = Pyt2Type.tp_traverse = (traverseproc) traverse;
Pyt1Type.tp_clear = Pyt2Type.tp_clear = (inquiry) clear;
Pyt1Type.tp_members = Pyt2Type.tp_members = members;
Pyt1Type.tp_new = new;
if (PyType_Ready(&Pyt1Type) < 0) return;
if (PyType_Ready(&Pyt2Type) < 0) return;
m = Py_InitModule("pyt", methods);
Py_INCREF(&Pyt1Type), PyModule_AddObject(m, "Pyt", (PyObject *) &Pyt1Type);
}
Используя мой тестовый скрипт
from distutils.core import Extension, setup
import sys, gc
sys.argv.extend(["build_ext", "-i"])
setup(ext_modules = [Extension('pyt', ['pyt.c'])])
from pyt import Pyt
pyt = Pyt()
print pyt, sys.getrefcount(pyt)
pyt = pyt.other
print pyt, sys.getrefcount(pyt)
del pyt
gc.collect()
Я получаю выход как
<pyt.Pyt1 object at 0x7fbc26540138> 3
<pyt.Pyt2 object at 0x7fbc26540150> 3
объекты не удаляются в конце, так как каждый хранит ссылку на другой, создавая замкнутый цикл. В другом коде я использовал подход, где я просто сохранил объекты, до тех пор, пока оба не будут иметь значение 0, которое, как я подозреваю, является плохой практикой. Теперь я попытался использовать сборщик мусора здесь, но все же объекты не собираются.
Что здесь общего не работает? Что я упустил?
я обновил вопрос, который реализует интерфейс сборщика мусора; еще нет сбор. – tynn