以python函数为参数调用Cython(C)函数



我想将本地定义的python函数与gsl库集成。为此,我用Cython实现了以下代码(例如高斯-勒让德正交(:

pxd文件:

cdef extern from "gsl/gsl_math.h":
ctypedef struct gsl_function:
double (* function) (double x, void * params) nogil
void * params
cdef extern from "gsl/gsl_integration.h":
gsl_integration_glfixed_table * gsl_integration_glfixed_table_alloc(size_t n) nogil 
double gsl_integration_glfixed(gsl_function *f, double a, double b, gsl_integration_glfixed_table * t) nogil
void  gsl_integration_glfixed_table_free(gsl_integration_glfixed_table *t) nogil 
cdef double int_gsl_GaussLegendre(double func(double, void *) nogil, void * p, double xmin, double xmax) nogil

和pyx文件:

cdef size_t size_GL=1000
cdef double int_gsl_GaussLegendre(double func(double, void *) nogil, void * p, double xmin, double xmax) nogil:
cdef double result, error;
cdef gsl_integration_glfixed_table * W 
cdef gsl_function F
W = gsl_integration_glfixed_table_alloc(size_GL)
F.function = func
F.params = p
result = gsl_integration_glfixed(&F, xmin, xmax, W)    
gsl_integration_glfixed_table_free(W)
return result

这段代码适用于我的Cython代码中声明的任何C函数。当然,当我将python函数作为参数传递时,这将失败。我在python中的脚本:

def gsl_integral(py_func, xmin, xmax, args=()):
cdef size_t sizep = <int>(args.size)
cdef double[:] params = np.empty(sizep, dtype=np.double)
for i in range(0,sizep):
params[i]=args[i]
cdef gsl_function F
F.function = py_func
F.params = params

其返回:";无法将Python对象转换为"double(*((double,void*(nogil";

如果我改为使用:

def gsl_integral(py_func, xmin, xmax, args=()):
cdef size_t sizep = <int>(args.size)
cdef double[:] params = np.empty(sizep, dtype=np.double)
for i in range(0,sizep):
params[i]=args[i]
cdef gsl_function F
F.function = <double *>py_func
F.params = params

其返回:";Python对象不能被强制转换为基元类型"的指针;

我已经看到我可以将我的cython函数包装成一个类(将c函数作为参数传递给cython中的python函数(,但我不太确定在这种情况下如何做到这一点(加上这个例子对我不起作用。(作为一种变通方法,我一直在向cython传递两个数组x和f(x(,后者用我的本地python f估计,为了定义一个我可以稍后集成的gsl样条曲线,但这一点都不优雅。还有其他办法吗?我想在没有GIL 的情况下使用GSL集成

非常感谢,罗曼

您的基本前提有几个基本问题:

  • C函数指针没有任何状态。Python可调用对象确实具有状态。因此,C函数指针没有的空间来存储调用Python函数所需的信息
  • 如果您正在调用Python可调用对象,则不能释放GIL,因为您必须具有GIL才能执行该对象

此答案概述了您的选项(C++std::function选项不适用于此处(。本质上,你可以选择

  • 使用ctypes从Python函数生成函数指针(它在运行时通过生成破解代码来实现这一点——这在标准C中是不可能的(
  • 或者编写一个具有适当签名的cdef函数作为函数指针传递,并将可调用的Python作为void*参数的一部分传递给它

我推荐后者,但两者都不是很好的选择。

最新更新