假设我定义了以下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'
cdef
方法Kernel
访问cdef
Run
方法,无需Python开销。目前我使用以下解决方案,但不满足上述要求:
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)