Давайте начнем с меньшего репродуктора:
%%cython
import numpy as np
from cython cimport floating
def func1(floating[:] X_data):
C = np.empty(12, dtype=np.int_32)
func2(X_data, C)
cpdef func2(floating[:] X_data, int[:] C):
pass
Не компилируется.
Одно важное замечание: func2
- это cpdef
, это означает, что Cython будет вызывать его как raw-C-функцию из func1
. Эти две С-сигнатуры будут созданы Cython для слитой функции func2
(одна для double
и одна для float
):
static PyObject *__pyx_fuse_0__pyx_f_4test_func2(__Pyx_memviewslice, __Pyx_memviewslice, int __pyx_skip_dispatch); /*proto*/
static PyObject *__pyx_fuse_1__pyx_f_4test_func2(__Pyx_memviewslice, __Pyx_memviewslice, int __pyx_skip_dispatch); /*proto*/
Таким образом, ожидается, что C
будет __Pyx_memviewslice
, но для Cython это PyObject
в func1
, поэтому функция не может быть вызвана как cdef
. Что я не понимаю: почему Cython не возвращается к python- def
версии?
Сигнатура C немного вводит в заблуждение, и Cython делает больше проверок типов во время компиляции, поэтому это не поможет определить C
как
cdef float[:] C
потому что даже если C
в этом случае также __Pyx_memviewslice
, он не имеет правильного типа, и только
cdef int[:] C
будет работать.
Если func2
было определено как
cpdef func2(floating[:] X_data, C):
соответствующие две С-подписи будут
static PyObject *__pyx_fuse_0__pyx_f_4test_func2(__Pyx_memviewslice, PyObject *, int __pyx_skip_dispatch); /*proto*/
static PyObject *__pyx_fuse_1__pyx_f_4test_func2(__Pyx_memviewslice, PyObject *, int __pyx_skip_dispatch); /*proto*/
, поэтому можно передать C
, что является PyObject
для этой функции.
Итак, есть два способа решения проблемы компиляции:
- используйте
cdef int[:] C
в func1
или
- падение
int[:] C
в подписи func2
Так почему же добавляется фиктивный параметр, т.е.
%%cython -a
import numpy as np
from cython cimport floating
def func1(floating[:] X_data, int dummy_variable=1):
C = np.empty(12, dtype=np.int_32)
func2(X_data, C, dummy_variable=dummy_variable)
cpdef func2(floating[:] X_data, int[:] C, int k=6, dummy_variable = 1):
pass
работает
На самом деле существует третий способ компиляции кода: сделать func2
функцию только для Python def
. В этом случае тип C
не играет роли во время компиляции и будет проверен во время выполнения.
Дело в том, что в случае с фиктивными переменными Cython решает вызвать func2
как Python-функцию, а не как C-функцию, и, таким образом, несоответствие типов не играет роли.
Это легко увидеть, просмотрев аннотированный html-файл.
Однако я не могу сказать, что является причиной того, что Cython прибегает к вызову Python-функции для вашего обходного пути. Все, что я могу сказать: не предоставление значения для k
играет важную роль.