首先要处理的是签名的函数
cdef void (*func)(double *, double *, double *)
. 您不知道这些数组有多长,因此无法安全访问它们的元素。明智的做法是更改函数签名以传递长度:
cdef void (*func)(double *, double *, double *, int)
更令人困惑的是,您似乎在两个方向上迭代1D数组的同一个轴
normal
和
sample
I've recommended the ctypes standard library module as a solution to similar problems previously
create a function pointer from a Python callable
. 如果你只想打电话,有一个更简单但更有限的解决方案
cdef
Cython函数。
下面是一个演示如何实现该想法的简单示例:
import numpy as np
import ctypes
ctypedef void (*func_t)(int, double *)
cdef void sample(int n, double* x, func_t f):
f(n,x)
def call_sample(double[::1] x,
f):
def func_wrapper(n, arg1):
# x is a slightly opaque ctypes type
# first cast it to a ctypes array of known size
# and then create a numpy array from that
arg1_as_ctypes_array = (ctypes.c_double*n).from_address(ctypes.addressof(arg1.contents))
return f(np.asarray(arg1_as_ctypes_array))
FTYPE = ctypes.CFUNCTYPE(None, # return type
ctypes.c_int, # arguments
ctypes.POINTER(ctypes.c_double))
f_ctypes = FTYPE(func_wrapper) # convert Python callable to ctypes function pointer
# a rather nasty line to convert to a C function pointer
cdef func_t f_ptr = (<func_t*><size_t>ctypes.addressof(f_ctypes))[0]
sample(x.shape[0], &x[0], f_ptr)
def example_function(x):
# expects a numpy array like object
print(x)
def test():
a = np.random.rand(20)
print(a)
call_sample(a,example_function)
我意识到ctypes和Cython之间有一些稍微混乱的转换-这是不可避免的。
一点解释:我假设您希望保持Python接口的简单,因此
example_function
只需要取一个类似numpy数组的对象。ctypes传递的函数需要接受许多元素和指针,以匹配C接口。
ctypes指针类型(
LP_c_double
arg1[5]
)因此,它可以很好地用于简单用途,但它不在内部存储其长度。将其更改为numpy数组很有帮助(但不是必需的),这样您可以更广泛地使用它,因此我们创建了一个包装函数来实现这一点。我们做到了:
arg1_as_ctypes_array = (ctypes.c_double*n).from_address(ctypes.addressof(arg1.contents))
np.asarray(arg1_as_ctypes_array)
将其转换为numpy数组。这将共享数据而不是复制数据,因此如果您更改它,则原始数据将更改。由于到numpy数组的转换遵循标准模式,因此很容易在中生成包装函数
call_sample
.
double
,不是
double*
. 在这种情况下,你不必做任何事情,因为一个ctypes
双重的
只有
功能
如果您确定要传递的函数将始终是
然后,您可以避免使用ctypes,并提出一些更简单的方法。首先需要使函数签名与指针完全匹配:
cdef void normal(int N, double *x): # other parameters as necessary
cdef double[::1] x_as_mview = <double[:N:1]>x # cast to a memoryview
# ... etc
SampleFunc
与创建模块级对象几乎相同:
# in Cython
normal_samplefunc = SampleFunc()
normal_samplefunc.func = &normal
# in Python
s=ars.foo( x, hx, hpx, normal_samplefunc, num)
ars.foo
是你写的方式(不是
ctypes
func = sample_func.func
# ...
sample(..., func,...)
此代码将运行得更快,但您希望能够调用
典型的
Python接口
典型的
来自Python。Python函数和传递给C的函数可能需要不同的接口,因此我将为这两种用途定义一个单独的函数,但共享实现:
def normal(double[::1] u, # ... other arguments
):
# or cpdef, if you really want
implementation goes here
# then, depending on if you're using ctypes or not:
def normal_ctypes(int n, u # other arguments ...
):
u_as_ctypes_array = (ctypes.c_double*n).from_address(ctypes.addressof(x.contents))
normal(u_as_ctypes_array, # other arguments
)
# or
cdef void normal_c(int n, double* u # ...
):
normal(<double[:N:1]>x # ...
)