使用pyx/pxd文件中的函数工厂为c库生成cython函数包装器



我正在重新评估将外部C库包装到Python中的不同方法。我很久以前就选择使用普通的Python C API,它快速、简单、独立,而且正如我所认为的那样,是面向未来的。然后我偶然发现了PyPy,它显然不打算支持CPython API,但将来可能会成为一个有趣的选择…因此,我正在寻找一个更高层次的切入点。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函数",指的是_fun_factory里面的cpdef。这里的问题是什么?我认为pyx文件就像普通的python文件一样。除了从单独的python脚本(如setup.py)动态生成pyx文件之外,是否有其他方法可以使此工作?

我也很惊讶cython不让我这样做:

ctypedef ndarray[double_t, ndim=1] p_t

来清理代码。为什么这行不通呢?

我知道有自动C -> cython翻译器,但我不愿意让自己依赖于这样的第三方工具。但如果您认为它已经可以用于生产,请随时提出建议。

pyx文件在可以匹配C和Python函数的意义上不像Python文件,并且对于使用C (cdefcpdef)函数可以做什么有一些限制。首先,您不能在运行时动态生成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是否适用于cimport 'd的C库,不过。你可能也想要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...

因此,似乎不可能对像my_c_library.fun1(&D)这样的表达式使用任何Python操作,这些表达式显然需要按原样输入。工厂只能在第二次传递时使用,当已经生成了第一组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预处理器宏感兴趣吗?

相关内容

  • 没有找到相关文章

最新更新