可以从Python脚本将cdef
Cython函数传递给另一个(python def
)cython函数吗?
最小例子:
test_module.pyx
cpdef min_arg(f, int N):
cdef double x = 100000.
cdef int best_i = -1
for i in range(N):
if f(i) < x:
x = f(i)
best_i = i
return best_i
def py_f(x):
return (x-5)**2
cdef public api double cy_f(double x):
return (x-5)**2
test.py
import pyximport; pyximport.install()
import testmodule
testmodule.min_arg(testmodule.py_f, 100)
这很有效,但我希望能够做到
testmodule.min_arg(testmodule.cy_f, 100)
来自test.py的,具有cython的速度(每个f(i)
调用没有Python开销)。但显然,Python并不了解cy_f,因为它不是def
或cpdef
声明的。
我希望存在这样的事情:
from scipy import LowLevelCallable
cy_f = LowLevelCallable.from_cython(testmodule, 'cy_f')
testmodule.min_arg(cy_f, 100)
但这会给TypeError: 'LowLevelCallable' object is not callable
。
提前谢谢。
答案 0 :(得分:1)
LowLevelCallable
是一类必须由底层Python模块接受的函数。这项工作已经完成了几个模块,包括正交例程scipy.integrate.quad
如果您希望使用相同的包装方法,则必须完成使用它的SciPy例程,例如scipy.ndimage.generic_filter1d
或scipy.integrate.quad
。但是,代码位于已编译的扩展中。
另一种方法是,如果你的问题被合理地定义为回调,那就是自己实现这个。我在我的一个代码中完成了这个,所以为了简单起见,我发布了链接:
.pxd
文件中,我定义了界面cyfunc_d_d
:https://github.com/pdebuyl/skl1/blob/master/skl1/core.pxd 最终代码允许简单的“cython-cython”调用,同时允许在Cython级别传递对象
我根据您的问题调整了代码:
test_interface.pxd
cdef class cyfunc:
cpdef double f(self, double x)
cdef class pyfunc(cyfunc):
cdef object py_f
cpdef double f(self, double x)
test_interface.pyx
cdef class cyfunc:
cpdef double f(self, double x):
return 0
def __cinit__(self):
pass
cdef class pyfunc(cyfunc):
cpdef double f(self, double x):
return self.py_f(x)
def __init__(self, f):
self.py_f = f
setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize
setup(
ext_modules=cythonize((Extension('test_interface', ["test_interface.pyx"]),
Extension('test_module', ["test_module.pyx"]))
)
)
test_module.pyx
from test_interface cimport cyfunc, pyfunc
cpdef min_arg(f, int N):
cdef double x = 100000.
cdef int best_i = -1
cdef int i
cdef double current_value
cdef cyfunc py_f
if isinstance(f, cyfunc):
py_f = f
print('cyfunc')
elif callable(f):
py_f = pyfunc(f)
print('no cyfunc')
else:
raise ValueError("f should be a callable or a cyfunc")
for i in range(N):
current_value = py_f.f(i)
if current_value < x:
x = current_value
best_i = i
return best_i
def py_f(x):
return (x-5)**2
cdef class cy_f(cyfunc):
cpdef double f(self, double x):
return (x-5)**2
使用:
python3 setup.py build_ext --inplace
python3 -c 'import test_module ; print(test_module.min_arg(test_module.cy_f(), 10))'
python3 -c 'import test_module ; print(test_module.min_arg(test_module.py_f, 10))'