В чем разница между tp_clear, tp_dealloc и tp_free?
У меня есть специальный модуль Python для поиска нечетких строк, реализующий вычисление расстояния Левенштейна, он содержит тип Python, называемый levtree, который имеет два члена - указатель на тип C wlevtree (называемый деревом), который выполняет все вычисления, и PyObject*, указывающий на список python-строк, называемый wordlist. Вот что мне нужно:
- когда я создаю новый экземпляр levtree, я использую конструктор, который принимает в качестве единственного входа кортеж строк (и это словарь, в котором экземпляр будет выполнять все поиски), этот конструктор должен будет создать новый экземпляр wordlist в новый экземпляр levtree и скопируйте содержимое входного кортежа в новый экземпляр wordlist. Вот мой первый фрагмент кода и мой первый вопрос:
static int
wlevtree_python_init(wlevtree_wlevtree_obj *self, PyObject *args, PyObject *kwds)
{
int numLines; /* how many lines we passed for parsing */
wchar_t** carg; /* argument to pass to the C function*/
unsigned i;
PyObject * strObj; /* one string in the list */
PyObject* intuple;
/* the O! parses for a Python object (listObj) checked
to be of type PyList_Type */
if (!(PyArg_ParseTuple(args, "O!", &PyTuple_Type, &intuple)))
{
return -1;
}
/* get the number of lines passed to us */
numLines = PyTuple_Size(intuple);
carg = malloc(sizeof(char*)*numLines);
/* should raise an error here. */
if (numLines < 0)
{
return -1; /* Not a list */
}
self->wordlist = PyList_New(numLines);
Py_IncRef(self->wordlist);
for(i=0; i<numLines; i++)
{
strObj = PyTuple_GetItem(intuple, i);
//PyList_Append(self->wordlist, string);
PyList_SetItem(self->wordlist, i, strObj);
Py_IncRef(strObj);
}
/* iterate over items of the list, grabbing strings, and parsing
for numbers */
for (i=0; i<numLines; i++)
{
/* grab the string object from the next element of the list */
strObj = PyList_GetItem(self->wordlist, i); /* Can't fail */
/* make it a string */
if(PyUnicode_Check(strObj))
{
carg[i] = PyUnicode_AsUnicode( strObj );
if(PyErr_Occurred())
{
return -1;
}
}
else
{
strObj = PyUnicode_FromEncodedObject(strObj,NULL,NULL);
if(PyErr_Occurred())
{
return -1;
}
carg[i] = PyUnicode_AsUnicode( strObj );
}
}
self->tree = (wlevtree*) malloc(sizeof(wlevtree));
wlevtree_init(self->tree,carg,numLines);
free(carg);
return 0;
}
Нужно ли вызывать Py_IncRef(self->wordlist); после self->wordlist = PyList_New(numLines); или это избыточно, потому что ссылки уже увеличены в PyList_new? Тогда у меня есть такое же сомнение в PyList_SetItem(self->wordlist, i, strObj); и Py_IncRef (strObj);..
-Когда я уничтожаю экземпляр levtree, я хочу вызвать функцию C, которая освобождает пространство, занимаемое деревом, уничтожает список слов и уменьшает количество ссылок на все строки, содержащиеся в списке слов. Вот мой tp_dealloc:
static void
wlevtree_dealloc(wlevtree_wlevtree_obj* self)
{
//wlevtree_clear(self);
if(self->tree!=NULL)
{
wlevtree_free(self->tree);
}
free(self->tree);
PyObject *tmp, *strObj;
unsigned i;
int size = PyList_Size(self->wordlist);
for(i=0; i<size; i++)
{
strObj = PyList_GetItem(self->wordlist, i);
Py_CLEAR(strObj);
}
Py_CLEAR(self->wordlist);
Py_TYPE(self)->tp_free((PyObject *)self);
}
Правильно ли заставить все освобождение работать здесь? На данный момент у меня нет tp_clear и tp_free, они мне нужны? В настоящий момент мой код работает с распределением, но не с освобождением, потому что, хотя я могу вызывать init для одной и той же переменной Python более одного раза, в конце каждого сценария Python (который работает правильно) я получаю "Ошибка сегментации", которая заставляет меня думаю, что что-то в процессе освобождения идет не так..
1 ответ
tp_clear
нужен только если вы реализуете циклический сборщик мусора. Похоже, что это не нужно, потому что вы поддерживаете только ссылки на объекты Unicode Python.
tp_dealloc
вызывается, когда счетчик ссылок объекта падает до нуля. Здесь вы уничтожаете объект и его участников. Затем он должен освободить память, занятую объектом, вызвав tp_free
,
tp_free
где память для объекта освобождается. Реализуйте это, только если вы реализуете tp_alloc
сам.
Причина разделения между tp_dealloc
а также tp_free
в том, что если ваш тип является подклассом, то только подкласс знает, как была выделена память и как правильно ее освободить.
Если ваш тип является подклассом существующего типа, ваш tp_dealloc
может понадобиться позвонить tp_dealloc
производного класса, но это зависит от деталей дела.
Подводя итог, кажется, что вы правильно обрабатываете уничтожение объектов (за исключением того, что вы carg
при выходе из функции с ошибкой).