2017-09-04 86 views
1

可以通过Python脚本将一个cdef Cython函数传递给另一个(python def)cython函数吗?通过python接口传递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,因为它不是defcpdef宣布。

我希望这样的事情存在:

from scipy import LowLevelCallable 
cy_f = LowLevelCallable.from_cython(testmodule, 'cy_f') 
testmodule.min_arg(cy_f, 100) 

但是这给TypeError: 'LowLevelCallable' object is not callable

预先感谢您。

回答

1

LowLevelCallable是一类必须被底层Python模块接受的函数。这项工作已经做了几个模块,包括正交常规scipy.integrate.quad

如果您希望使用相同的包装方法,则必须经过SciPy的例程,使用它,如scipy.ndimage.generic_filter1dscipy.integrate.quad。但是,代码位于编译扩展中。

另一种方法是,如果您的问题在回调中定义合理,则自行实施。我已经在我的代码中的一个做这一点,所以我张贴的链接简单:

  1. .pxd文件中,我定义了接口cyfunc_d_dhttps://github.com/pdebuyl/skl1/blob/master/skl1/core.pxd
  2. 我可以重新使用该界面中的“基础“cython模块https://github.com/pdebuyl/skl1/blob/master/skl1/euler.pyx以及”用户定义“模块。

最后的代码,使简单的 “用Cython,用Cython” 要求,同时允许对象的传球在用Cython水平

我适应代码到你的问题:

  1. 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)                           
    
  2. 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 
    
  3. 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"]))                 
            )                              
    )                                   
    
  4. 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))'