通过python接口传递cython函数 [英] Pass cython functions via python interface
问题描述
能否将 cdef
Cython函数从Python脚本传递给另一个(python def
)Cython函数? / p>
最小示例:
test_module.pyx
cpdef min_arg(f,int N):
cdef double x = 100000.
cdef int best_i = -1
$ i在范围(N)中的b $ b:如果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()
导入测试模块
testmodule.min_arg(testmodule.py_f,100)
这很好用,但我希望也能做到
testmodule.min_arg(testmodule .cy_f,100)
来自test.py,具有cython的速度(每个都没有Python开销) f(i)
电话)。但是显然,Python不了解cy_f,因为它不是声明的 def
或 cpdef
。
我希望这样的东西存在:
从scipy import LowLevelCallable
cy_f = LowLevelCallable.from_cython(testmodule,'cy_f')
testmodule.min_arg(cy_f,100)
但这会导致 TypeError:'LowLevelCallable'对象不可调用
。
请先谢谢您。
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模块 https://中重用此接口github.com/pdebuyl/skl1/blob/master/skl1/euler.pyx ,也位于用户定义的模块中。
最终代码进行简单的 cython-cython调用,同时允许在Cython级别传递对象
我针对您的问题修改了代码:
-
test_interface.pxd
cdef类cyfunc:
cpdef double f(self,double x)
cdef class pyfunc(cyfunc):
cdef对象py_f
cpdef double f(self,double x)
-
test_interface.pyx
cdef类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
从setuptools导入安装程序,扩展
从Cython生成导入cythonize
设置(
ext_modules = cythonize((Extension('test_interface',[ test_interface.pyx]),
Extension('test_module',[ test_module.pyx]))
))
)
-
test_module.pyx
来自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可调用(f):
py_f = pyfunc(f)
print('no cyfunc')
else:
提高ValueError( f应该是可调用的或cyfunc)
对于范围(N)中的i:
current_value = py_f.f( i)如果current_value<
x:
x =当前值
best_i = i
return best_i
def py_f(x):
return(x-5)** 2
cdef类cy_f(cyfunc):
cpdef double f(self,double x):
返回(x-5)** 2
要使用:
python3 setup.py build_ext --inplace
python3 -c'导入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))’
Can a cdef
Cython function be passed to another (python def
) cython function from a Python script?
Minimal example:
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)
This works well, but I want to be able to also do
testmodule.min_arg(testmodule.cy_f, 100)
from a test.py, to have cython's speed (no Python overhead for each f(i)
call). But obviously, Python doesn't know about cy_f, because it's not def
or cpdef
declared.
I was hoping something like this existed:
from scipy import LowLevelCallable
cy_f = LowLevelCallable.from_cython(testmodule, 'cy_f')
testmodule.min_arg(cy_f, 100)
But this gives TypeError: 'LowLevelCallable' object is not callable
.
Thank you in advance.
The LowLevelCallable
is a class of functions that must be accepted by the underlying Python module. This work has been done for a few modules, including the quadrature routine scipy.integrate.quad
If you wish to use the same wrapping method, you must either go through the SciPy routines that make use of it, such as scipy.ndimage.generic_filter1d
or scipy.integrate.quad
. The code sits in compiled extensions, however.
The alternative, if your problem is reasonably well defined for the callback, is to implement this yourself. I have done this in one of my codes, so I post the link for simplicity:
- In a
.pxd
file, I define the interfacecyfunc_d_d
: https://github.com/pdebuyl/skl1/blob/master/skl1/core.pxd - I can re-use this interface in the "base" cython module https://github.com/pdebuyl/skl1/blob/master/skl1/euler.pyx and also in a "user-defined" module.
The final code makes plain "cython-cython" calls while allowing the passing of objects at the Cython level
I adapted the code to your problem:
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
To use:
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))'
这篇关于通过python接口传递cython函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!