Я пишу расширение C для моей программы Python для скорости и запускаю какое-то очень странное поведение, пытающееся перейти в 3-мерный массив numpy. Он работает с 2-мерным массивом, но я уверен, что я что-то прикрутил указателями, пытаясь заставить его работать с 3-м измерением. Но вот странная часть. Если я просто перехожу в трехмерный массив, он сбой с Ошибка шины. Если (в Python) я сначала создаю свою переменную как 2D-массив, а затем перезаписываю ее с помощью 3D-массива, работает отлично. Если переменная является пустым массивом сначала, а затем 3D-массив, он сбой с Seg Fault. Как это может случиться?Передача 3-мерного массива numpy на C
Кроме того, может ли кто-нибудь помочь мне получить 3D-массив? Или я должен просто сдаться и пройти в 2D-массив и изменить его сам?
Вот мой C код:
static PyObject* func(PyObject* self, PyObject* args) {
PyObject *list2_obj;
PyObject *list3_obj;
if (!PyArg_ParseTuple(args, "OO", &list2_obj, &list3_obj))
return NULL;
double **list2;
double ***list3;
//Create C arrays from numpy objects:
int typenum = NPY_DOUBLE;
PyArray_Descr *descr;
descr = PyArray_DescrFromType(typenum);
npy_intp dims[3];
if (PyArray_AsCArray(&list2_obj, (void **)&list2, dims, 2, descr) < 0 || PyArray_AsCArray(&list3_obj, (void ***)&list3, dims, 3, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "error converting to c array");
return NULL;
}
printf("2D: %f, 3D: %f.\n", list2[3][1], list3[1][0][2]);
}
А вот мой Python код, который вызывает функции выше:
import cmod, numpy
l2 = numpy.array([[1.0,2.0,3.0], [4.0,5.0,6.0], [7.0,8.0,9.0], [3.0, 5.0, 0.0]])
l3 = numpy.array([[2,7, 1], [6, 3, 9], [1, 10, 13], [4, 2, 6]]) # Line A
l3 = numpy.array([]) # Line B
l3 = numpy.array([[[2,7, 1, 11], [6, 3, 9, 12]],
[[1, 10, 13, 15], [4, 2, 6, 2]]])
cmod.func(l2, l3)
Так что, если я закомментируйте обе линии А и В, он падает с Ошибка шины. Если строка A есть, но строка B закомментирована, она работает правильно, без ошибок. Если строка B есть, но строка A закомментирована, она печатает правильные номера, но затем Seg faults. Наконец, если обе строки присутствуют, они также печатают правильные цифры, а затем Seg faults. Что, черт возьми, здесь происходит?
EDIT: Ok. Вау. Поэтому я использовал int
в Python, но набрал их double
в C. И это отлично работало с 1D и 2D массивами. Но не 3D. Поэтому я изменил определение Python l3 на float, и теперь все работает фантастически (Большое спасибо Bi Rico).
Но теперь более странное поведение с линиями A & B! Теперь, если обе строки закомментированы, программа работает. Если присутствует строка B, но A закомментирован, она работает, а если обе раскоментированы. Но если строка A присутствует и B закомментирован, я снова получаю эту фантастическую ошибку шины. Я бы очень хотел избежать этого в будущем, так и есть ли у кого-нибудь понять, почему объявление переменной Python может иметь такое влияние?
EDIT 2:. Ну, как безумные, как эти ошибки, они все из-за 3-мерный массив Numpy я прохожу в случае, если я прохожу только в 1- или 2-D массивов, он ведет себя как ожидается, и манипуляции с другими переменными Python ничего не делают. Это заставляет меня думать, что проблема лежит где-то в подсчете ссылок Python. В C-коде счетчик ссылок уменьшается больше, чем нужно для трехмерных массивов, и когда эта функция возвращает Python пытается очистить объекты и пытается удалить указатель NULL. Это только моя догадка, и я пытался сделать Py_INCREF();
все, что я мог придумать безрезультатно. Я думаю, я просто использовать 2D-массив и видоизменение его в С.
Вы уверены, что '(аннулируются **)' правильно, ты не должен просто пройти в a '(void *)'? – seberg
My C отстой, но ... Разве ваше выражение в 'if' не закорачивается, если первый вызов' PyArray_AsCArray' преуспевает? Вполне возможно, что второй вызов, т. Е. Один для 'list3', никогда не выполняется. – Jaime
@seberg Я не уверен, что '(void **)' правильный, но '(void *)' вызывает ошибку шины. @Jaime Нет, эта функция возвращает отрицательные значения только в случае сбоя, скорее всего, если вызов malloc вызывает отказ. – DaveTheScientist