Cython:将扩展类型序列作为另一个扩展类型的属性,可以访问cdef方法

时间:2015-06-29 15:22:04

标签: python c++ cython

假设我定义了以下Cython类

cdef class Kernel:
    cdef readonly double a

    def __init__(self, double a):
        self.a = a

    cdef public double GetValue(self, double t):
        return self.a*t 

现在我想定义另一个具有一系列内核作为属性的扩展类型。类似的东西:

cdef class Model:
    cdef readonly Kernel[:] kernels
    cdef unsigned int n_kernels

    def __init__(self, Kernel[:] ker):
        self.kernels = ker
        self.n_kernels = ker.shape[0]

    cdef double Run(self, double t):

        cdef int i
        cdef double out=0.0

        for i in range(self.n_kernels):
            out += self.kernels[i].GetValue(t)

        return out

然而,这不起作用。首先,我需要将Kernel[:]替换为object[:],否则我会从gcc

中收到以下错误
‘PyObject’ has no member named ‘__pyx_vtab’

如果我使用object[:],一切都编译得很好,但在尝试访问GetValue方法时出错:

AttributeError: "AttributeError: "'cytest.Kernel' object has no attribute 'GetValue'" in 'cytest.Model.Run'

我想要什么

  1. cdef方法Kernel访问cdef Run方法,无需Python开销。
  2. 键入 - 检查内核的元素。
  3. 我目前的解决方法

    目前我使用以下解决方案,但不满足上述要求:

    cdef class Kernel:
        cdef readonly double a
    
        def __init__(self, double a):
            self.a = a
    
        cpdef public double GetValue(self, double t):
            return self.a*t 
    
    cdef class Model:
        cdef readonly object[:] kernels
        cdef unsigned int n_kernels
    
        def __init__(self, object[:] ker):
            self.kernels = ker
            self.n_kernels = ker.shape[0]
    
        def Run(self, double t):
    
            cdef int i
            cdef double out=0.0
    
            for i in range(self.n_kernels):
                out += self.kernels[i].GetValue(t)
    
            return out
    

    即。我将Kernel类的方法声明为cpdef,以便可以从Python访问它们并使用object[:]

    问题

    有没有办法在没有Python开销的情况下在Cython中实现上面的第1点和第2点?

    提前感谢您的时间。

    注意:我事先并不知道序列的长度。

    修改

    关注@DavidW建议我修改了代码如下

    # module cytest
    import cython
    
    cdef class Kernel:
        cdef readonly double a
    
        def __init__(self, double a ):
            self.a = a
    
        cdef public double GetValue(self, double t):
            return self.a*t
    
    
    cdef class Model:
        cdef readonly Kernel[:] kernels
        ### added this attribute 
        cdef Kernel k 
        cdef unsigned int n_kernels
    
        def __cinit__(self, Kernel[:] ker):
            self.kernels = ker
            self.n_kernels = ker.shape[0]
    
        cpdef double Run(self, double t):
    
            cdef int i
            cdef double out=0.0
    
            for i in range(self.n_kernels):
                # now i assign to the new attribute each time 
                # and access the cdef method from it
                self.k = self.kernels[i]
                out += self.k.GetValue(t)
    
            return out
    

    现在它编译并运行正常(并且比我以前的解决方法更快),即使在访问Kernel[:]属性时仍然有一些python开销。

    我在这里建立了一个构建和调用Model

    的示例
    import cytest
    import numpy as np
    
    ker_list = [cytest.Kernel(i*1.0) for i in range(3)]
    
    # transform it to a numpy array
    # to be able to pass it to the 'Model' constructor
    ker_arr = np.array(ker_list)
    
    # create a model instance
    model = cytest.Model(ker_arr)
    
    # call the method Run
    print model.Run(1.0)
    

0 个答案:

没有答案