Cython: невозможно преобразовать объект Python в 'double *'
Я пишу обертку Cython для функции C. У меня есть файл pxd со следующей подписью:
double contr_hrr(int lena, double xa, double ya, double za, double *anorms)
Когда я пытаюсь вызвать это из файла pyx
...
return contr_hrr(len(acoefs),a.origin[0],a.origin[1],a.origin[2],anorms2)
где anorms2 - это список Python, я получаю сообщение об ошибке:
cython/ctwo.pyx:35:80: Cannot convert Python object to 'double *'
Как передать список Python в функцию C в виде двойного массива?
3 ответа
cimport
array
:from cpython cimport array
Создайте объект массива из вашего списка. Конструктор класса массива будет выполнять всю тяжелую работу, выделяя память и итерируя по вашему списку (на самом деле может быть любым итеративным).
cdef array.array anorms2_arr = array.array('d', anorms2)
Передайте его данные вашей функции:
return contr_hrr(.., anorms2_arr.data.as_doubles)
array
стандартный модуль Python Cython добавляет некоторую специальную поддержку сверху, такую как буферный интерфейс и прямой доступ к базовому блоку памяти через arr.data.as_xxx
, К сожалению, эта поддержка документирована только здесь. Вы также можете найти некоторые подробности об использовании массива в этой недавней теме.
Я думаю, что вы не можете сделать иначе, но конвертировать это самостоятельно:
cimport cython
from libc.stdlib cimport malloc, free
...
cdef double *anorms
cdef unsigned int i;
anorms = <double *>malloc(len(anorms2)*cython.sizeof(double))
if anorms is NULL:
raise MemoryError()
for i in xrange(len(anorms2)):
anorms[i] = anorms2[i]
return contr_hrr(len(acoefs),a.origin[0],a.origin[1],a.origin[2],anorms)
Если бы вы были в C++, это было бы иначе, потому что
The following coercions are available:
Python type => C++ type => Python type
bytes std::string bytes
iterable std::vector list
iterable std::list list
iterable std::set set
iterable (len 2) std::pair tuple (len 2)
Если бы вы могли переключиться на C++, вы бы имели прямой перевод с List[float]
в vector<double>
:
from libcpp.vector cimport vector
def py_contr_hrr(vector[double] anorms2, ...):
...
return contr_hrr(len(acoefs),a.origin[0],a.origin[1],a.origin[2],anorms2)
И звонить прямо со стороны Python:
anorms2 = [12.0, 0.5, ...]
py_contr_hrr(anorms2, ....)
Источник: http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html
Но я не знаю, если это вариант, который вы можете рассмотреть... Конечно, это зависит от ограничений вашего проекта.
РЕДАКТИРОВАТЬ: я не знал о Nikita
Это способ работы (кстати, элегантный), и я не знаю, какой эфир лучше всего подходит для исполнения на больших массивах.
В конце концов я убедился, что массивы anorms были сохранены в виде массивов в части кода Python, а затем следовали рецепту Никиты, чтобы преобразовать их на лету в удвоенные значения, используя свойство.data.as_doubles. Если я это сделаю, то у него очень мало накладных расходов по сравнению с тем, как все делать изначально в C.
Еще не экспериментировал с непристойным подходом по ряду обыденных причин.