В чем разница между 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 при выходе из функции с ошибкой).

Другие вопросы по тегам