C-расширение в Python - вернуть Py_BuildValue () проблема утечки памяти - PullRequest
8 голосов
/ 01 апреля 2011

У меня огромная проблема с утечкой памяти, связанная с разрабатываемым C-расширением. В C у меня есть массив значений типа double с именем A и переменная int с именем AnotherIntVariable, которую я хочу передать в Python. Ну, в моем модуле C-extension я делаю следующее:

int i;  
PyObject *lst = PyList_New(len_A);  
PyObject *num;  
if(!lst)  
   return NULL;  
for(i=0;i<len_A;i++){  
   num=PyFloat_FromDouble(A[i]);  
   if(!num){  
      Py_DECREF(lst);  
      return NuLL;  
   }  
   PyList_SET_ITEM(lst,i,num);  
}  
free(A);  
return Py_BuildValue("Oi",lst,AnotherIntVariable)

Итак, в Python я получаю этот список и int вроде этого:

Pyt_A,Pyt_int=MyCModule.MyCFunction(...)

Где Pyt_A и Pyt_int - это список и целое число, которое я получаю из C-расширения "MyCModule", из функции "MyCFunction", которую я описал ранее.

Проблема заключается в том, что в Python я использую этот массив Pyt_A (поэтому я использую Py_BuildValue вместо простого оператора return, чтобы выполнить INCREF, чтобы на мгновение сохранить эту переменную из сборщик мусора) но тогда мне нужно как-то разыменовать его, чтобы освободить выделенную память. Проблема в том, что я использую функцию MyCFunction несколько раз, и это вызывает утечку памяти, потому что я не знаю, как разыменовать массив, который я получаю в python, чтобы избавиться от него.

Я попытался просто вернуть массив, выполнив return lst в части кода C вместо Py_BuildValue("Oi",lst,AnotherIntVariable), но это приводит к ошибке сегментации, когда я пытаюсь использовать его в python (возможно, из-за мусора). коллекционер сделал свое дело) ...

... что мне здесь не хватает? Кто-нибудь может мне помочь?

Ответы [ 2 ]

11 голосов
/ 01 апреля 2011

Если вы посмотрите на документацию для Py_BuildValue (http://docs.python.org/3/c-api/arg.html#c.Py_BuildValue), то увидите, что при типе O это говорит о том, что счетчик ссылок переданного объекта увеличивается на единицу (Примечание:в предыдущем разделе на этой странице описан тип-код O для PyArg_ParseTuple, который не не увеличивает счетчик ссылок, но здесь также не имеет значения).

Итак, послепозвоните по номеру Py_BuildValue, счет для вашего списка будет 2, но вы хотите, чтобы он был 1.

Вместо того, чтобы возвращать результат Py_BuildValue напрямую, сохраните его в PyObject указатель, уменьшите счетчик ссылок lst, а затем верните свой результат.

Вы все равно должны проверять результат вызова Py_BuildValue, поскольку вам также необходимо освободить num в случае, если Py_BuildValue терпит неудачу (т.е. возвращает NULL).

2 голосов
/ 01 апреля 2011

Спасибо, что прояснил Игнасио, теперь это так важно!Наконец, решение состояло в том, чтобы вместо прямого возврата Py_BuildValue сделать:

free(A);  
PyObject *MyResult = Py_BuildValue("Oi",lst,AnotherIntVariable);  
Py_DECREF(lst);  
return MyResult

Это сработало как шарм!

...