问题
Suppose I defined the following Cython class
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
Now I'd like to define another Extension Type that has a sequence of Kernels as attribute. Something like:
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
However, this doesn't work. First I need to substitute Kernel[:]
with object[:]
, otherwise i get the following error from gcc
‘PyObject’ has no member named ‘__pyx_vtab’
If I use object[:]
everything compiles fine but I get an error when attempting to access GetValue
method:
AttributeError: "AttributeError: "'cytest.Kernel' object has no attribute 'GetValue'" in 'cytest.Model.Run'
What I would like
- Access the
cdef
methods ofKernel
from thecdef
methodRun
, without Python overhead. - Type-check the elements of kernels.
My current workaround
At the moment I use the following solution which however doesn't satisfy the requirements above:
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
i.e. I declare the methods of the Kernel class as cpdef
so that they can be accessed to from Python and use object[:]
.
Question
Is there a way to achieve points 1 and 2 above in Cython without Python overhead?
Thanks in advance for your time.
NB: I don't know the length of the sequence in advance.
Edit
Following @DavidW advice I modified the code as follows
# 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
Now it compiles and runs fine (and faster than my previous workaround), even if I still have some python overhead when accessing the Kernel[:]
attribute.
I put here an example of building and calling a 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)
来源:https://stackoverflow.com/questions/31119510/cython-have-sequence-of-extension-types-as-attribute-of-another-extension-type