Использование фабрики функций в файлах pyx/pxd для генерации оболочек функций Cython для библиотеки C
Я переоцениваю различные способы обернуть внешние библиотеки C в Python. Я давно решил использовать простой Python C API, который был быстрым, простым, автономным и, как я думал, ориентирован на будущее. Потом наткнулся на PyPy
, который, очевидно, не планирует поддерживать API CPython, но может стать интересной альтернативой в будущем... Поэтому я ищу точку входа более высокого уровня. ctypes
был медленным, так что теперь я вернулся в cython
, который, кажется, пытается поддержать PyPy.
В моей библиотеке много функций с одной и той же сигнатурой, поэтому я широко использовал макросы препроцессора C для генерации модуля Python. Я думал, что в Cython это станет намного удобнее, так как у меня будет доступ ко всему языку Python. Однако у меня возникают проблемы при написании фабрики для моих функциональных оболочек:
import cython
from numpy cimport ndarray, double_t
cimport my_c_library
cdef my_c_library.data D
ctypedef double_t DTYPE_t
cdef parse_args(ndarray[DTYPE_t] P, ndarray[DTYPE_t] x, ndarray[DTYPE_t] y):
D.n = P.size
D.m = x.size
D.P = <double*> P.data
D.x = <double*> x.data
D.y = <double*> y.data
def _fun_factory(name):
cpdef fun(ndarray[DTYPE_t] P, ndarray[DTYPE_t] x, ndarray[DTYPE_t] y):
parse_args(P, x, y)
getattr(my_c_library, name)(&D)
return y
return fun
fun1 = _fun_factory('fun1')
fun2 = _fun_factory('fun2')
# ... many more function definitions ...
Компилятор Cython жалуется: "Определение функции C здесь запрещено", ссылаясь на cpdef
внутри _fun_factory
, В чем здесь проблема? я думал pyx
файлы были как обычные файлы Python. Есть ли способ заставить это работать, кроме очевидного для генерации pyx
файл динамически из отдельного скрипта Python, например setup.py
?
Я также был удивлен, что Cython не позволил мне сделать:
ctypedef ndarray[double_t, ndim=1] p_t
очистить код. Почему это не работает?
Я в курсе, что есть автоматические C -> cython
переводчики там, но я не хочу ставить себя в зависимость от таких сторонних инструментов. Но, пожалуйста, не стесняйтесь предложить один, если вы думаете, что он готов к использованию.
2 ответа
pyx
файлы не похожи на файлы Python в том смысле, что вы можете сопоставить функции C и Python, и есть некоторые ограничения на то, что вы можете делать с C (cdef
или же cpdef
) функция. С одной стороны, вы не можете динамически генерировать код C во время выполнения, что и пытается сделать ваш код. поскольку fun
на самом деле просто выполняет некоторый код Python после проверки типов его аргументов, вы также можете сделать его обычной функцией Python:
def fun(P, x, y):
parse_args(P, x, y)
getattr(my_c_library, name)(&D)
return y
parse_args
будет делать ту же проверку аргументов, так что вы ничего не потеряете. (Я не уверен, getattr
работает над библиотекой C, это cimport
хотя бы. Вы можете захотеть import
это также.)
Для ctypedef
Это, вероятно, какое-то ограничение / ошибка в Cython, которую еще никто не успел исправить.
После игры еще немного, кажется, работает следующее:
def _fun_factory(fun_wrap):
def fun(P, x, y):
parse_args(P, x, y)
fun_wrap()
return y
return fun
def _fun1(): my_c_library.fun1(&D)
def _fun2(): my_c_library.fun2(&D)
# ... many more ...
fun1 = _fun_factory(_fun1)
fun2 = _fun_factory(_fun2)
# ... many more...
Так что, похоже, нет возможности использовать какие-либо операции Python для таких выражений, как my_c_library.fun1(&D)
который, видимо, нужно набирать как есть. Фабрика может использоваться только на втором проходе, когда уже сгенерирован первый набор оболочек Python. Это не более элегантно, чем очевидное:
cpdef fun1(ndarray[DTYPE_t] P, ndarray[DTYPE_t] x, ndarray[DTYPE_t] y):
parse_args(P, x, y)
my_c_function.fun1(&D)
return y
# ... many more ...
Вот, cpdef
можно использовать без проблем. Итак, я собираюсь использовать метод копирования-вставки... Кто-нибудь также заинтересован в макропроцессорах для Cython в будущем?