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

zol*_*zol 6 c++ python cython

假设我定义了以下 Cython 类

\n\n
cdef class Kernel:\n    cdef readonly double a\n\n    def __init__(self, double a):\n        self.a = a\n\n    cdef public double GetValue(self, double t):\n        return self.a*t \n
Run Code Online (Sandbox Code Playgroud)\n\n

现在我想定义另一个具有内核序列作为属性的扩展类型。就像是:

\n\n
cdef class Model:\n    cdef readonly Kernel[:] kernels\n    cdef unsigned int n_kernels\n\n    def __init__(self, Kernel[:] ker):\n        self.kernels = ker\n        self.n_kernels = ker.shape[0]\n\n    cdef double Run(self, double t):\n\n        cdef int i\n        cdef double out=0.0\n\n        for i in range(self.n_kernels):\n            out += self.kernels[i].GetValue(t)\n\n        return out\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,这不起作用。首先我需要替换Kernel[:]object[:],否则我会收到以下错误gcc

\n\n
\xe2\x80\x98PyObject\xe2\x80\x99 has no member named \xe2\x80\x98__pyx_vtab\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我使用一切编译正常,但在尝试访问方法object[:]时出现错误:GetValue

\n\n
AttributeError: "AttributeError: "\'cytest.Kernel\' object has no attribute \'GetValue\'" in \'cytest.Model.Run\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

我想要什么

\n\n
    \n
  1. 访问from方法cdef的方法,无需 Python 开销。KernelcdefRun
  2. \n
  3. 对内核元素进行类型检查。
  4. \n
\n\n

我目前的解决方法

\n\n

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

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

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

\n\n

问题

\n\n

有没有办法在 Cython 中实现上述第 1 点和第 2 点而不需要 Python 开销?

\n\n

在此先感谢您的时间。

\n\n

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

\n\n

编辑

\n\n

根据@DavidW的建议,我修改了代码如下

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

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

\n\n

我在这里举了一个构建和调用的示例Model

\n\n
import cytest\nimport numpy as np\n\nker_list = [cytest.Kernel(i*1.0) for i in range(3)]\n\n# transform it to a numpy array\n# to be able to pass it to the \'Model\' constructor\nker_arr = np.array(ker_list)\n\n# create a model instance\nmodel = cytest.Model(ker_arr)\n\n# call the method Run\nprint model.Run(1.0)\n
Run Code Online (Sandbox Code Playgroud)\n