在其他Cython代码中使用自定义Cython代码

12 投票
3 回答
2480 浏览
提问于 2025-04-16 13:51

我现在正在尝试优化我的Python程序,因此开始使用Cython来减少函数调用的开销,之后可能还会加入一些优化过的C语言库函数。

于是我遇到了第一个问题:

我在代码中使用了组合的方式来创建一个更大的类。目前我已经把我的一个Python类转换成了Cython(这已经够难的了)。以下是代码:

import numpy as np
cimport numpy as np
ctypedef np.float64_t dtype_t
ctypedef np.complex128_t cplxtype_t
ctypedef Py_ssize_t index_t

cdef class bendingForcesClass(object):
    cdef dtype_t bendingRigidity
    cdef np.ndarray matrixPrefactor
    cdef np.ndarray bendingForces

    def __init__(self, dtype_t bendingRigidity, np.ndarray[dtype_t, ndim=2] waveNumbersNorm):
        self.bendingRigidity = bendingRigidity
        self.matrixPrefactor = -self.bendingRigidity * waveNumbersNorm ** 2

    cpdef np.ndarray calculate(self, np.ndarray membraneHeight):
        cdef np.ndarray bendingForces
        bendingForces = self.matrixPrefactor * membraneHeight
        return bendingForces

在我组合的Python/Cython类中,我调用了类方法calculate,所以在我的组合类中有以下(简化的)代码:

from bendingForcesClass import bendingForcesClass

cdef class membraneClass(object):
    def  __init__(self, systemSideLength, lowerCutoffLength, bendingRigidity):
        self.bendingForces = bendingForcesClass(bendingRigidity, self.waveNumbers.norm)

    def calculateForces(self, heightR):
        return self.bendingForces.calculate(heightR)

我发现cpdef可以让方法/函数同时被Python和Cython调用,这很好用,只要我不提前定义self.bendingForces的类型——根据文档(为了速度的提前绑定),这是为了消除函数调用的开销。我尝试了以下代码,但不成功:

from bendingForcesClass import bendingForcesClass
from bendingForcesClass cimport bendingForcesClass

    cdef class membraneClass(object):
        cdef bendingForcesClass bendingForces

        def  __init__(self, systemSideLength, lowerCutoffLength, bendingRigidity):
            self.bendingForces = bendingForcesClass(bendingRigidity, self.waveNumbers.norm)

        def calculateForces(self, heightR):
            return self.bendingForces.calculate(heightR)

这样在尝试用Cython构建membraneClass.pyx时,我得到了这个错误:

membraneClass.pyx:18:6: 'bendingForcesClass' is not a type identifier
building 'membraneClass' extension

请注意,声明在两个不同的文件中,这让事情变得更复杂。

那么我该如何解决这个问题呢?如果有人能给我一点提示,我将非常感激,因为除了上面提供的链接,我找不到任何相关的信息。

谢谢,祝好!

3 个回答

2

这些可能不是错误的根源,但为了缩小问题范围,你可以尝试更改以下内容:

你是不是在这里使用了 bendingForces 作为变量的名字:

cpdef np.ndarray calculate( self, np.ndarray membraneHeight ) :
      cdef np.ndarray bendingForces
      bendingForces = self.matrixPrefactor * membraneHeight
      return bendingForces

同时在这里也用作成员对象的名字:

cdef class membraneClass( object ):
    cdef bendingForcesClass bendingForces

另外,bendingForcesClass 既是模块的名字,也是类的名字。最后,考虑从类 bendingForcesClass 创建一个 ctypedef 吗?

4

你需要使用一个声明文件,后缀是 ".pxd",并且要用到 cimport。简单来说,cimport 是在编译的时候用的,而 import 是在运行的时候用的,所以 Cython 不能利用一些重要的东西。

首先,创建一个 "utils.pxd" 文件:

cdef class MyClass:
    cdef readonly int field
    cdef void go(self, int i)

接下来,"utils.pyx" 文件现在的内容是:

cdef class MyClass:
    def __init__(self, field):
    self.field = field

cdef void go(self, int i):
    self.field = i

所有在 pyx 文件中的声明都要放到 .pxd 文件里。

然后在 mymodule.pyx 文件中:

from utils import MyClass
from utils cimport MyClass
# other code follows...

// 从这里开始的详细回答: Cython:在类型声明中使用导入的类

7

免责声明:这个问题很旧了,我不确定现在的解决方案是否适用于2011年的Cython代码。

要从另一个文件中引入一个扩展类(cdef class),你需要提供一个.pxd文件(也叫做定义文件),这个文件里要声明所有的C类、属性和方法。具体可以参考文档中的共享扩展类型部分。

以你的例子为例,你需要一个名为bendingForcesClass.pxd的文件,这个文件里要声明你想要共享的类,以及所有的cimport、模块级变量、类型定义等等:

bendingForcesClass .pxd
# cimports
cimport numpy as np

# typedefy you want to share
ctypedef np.float64_t dtype_t
ctypedef np.complex128_t cplxtype_t
ctypedef Py_ssize_t index_t

cdef class bendingForcesClass:
    # declare C attributes
    cdef dtype_t bendingRigidity
    cdef np.ndarray matrixPrefactor
    cdef np.ndarray bendingForces

    # declare C functions
    cpdef np.ndarray calculate(self, np.ndarray membraneHeight)

    # note that __init__ is missing, it is not a C (cdef) function

现在在.pxd文件中声明的所有导入、变量和属性,都可以(也必须)从.pyx文件中删除:

bendingForcesClass .pyx
import numpy as np

cdef class bendingForcesClass(object):

    def __init__(self, dtype_t bendingRigidity, np.ndarray[dtype_t, ndim=2] waveNumbersNorm):
        self.bendingRigidity = bendingRigidity
        self.matrixPrefactor = -self.bendingRigidity * waveNumbersNorm ** 2

    cpdef np.ndarray calculate(self, np.ndarray membraneHeight):
        cdef np.ndarray bendingForces
        bendingForces = self.matrixPrefactor * membraneHeight
        return bendingForces

这样,你的cdef类bendingForcesClass就可以从其他Cython模块中被引入,成为一个有效的类型标识符,这样就能解决你的问题了。

撰写回答