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 ответа

Решение
  1. cimport array:

    from cpython cimport array
    
  2. Создайте объект массива из вашего списка. Конструктор класса массива будет выполнять всю тяжелую работу, выделяя память и итерируя по вашему списку (на самом деле может быть любым итеративным).

    cdef array.array anorms2_arr = array.array('d', anorms2)
    
  3. Передайте его данные вашей функции:

    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.

Еще не экспериментировал с непристойным подходом по ряду обыденных причин.

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