在其他Cython代码中使用自定义Cython代码
我现在正在尝试优化我的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 个回答
这些可能不是错误的根源,但为了缩小问题范围,你可以尝试更改以下内容:
你是不是在这里使用了 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
吗?
你需要使用一个声明文件,后缀是 ".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:在类型声明中使用导入的类
免责声明:这个问题很旧了,我不确定现在的解决方案是否适用于2011年的Cython代码。
要从另一个文件中引入一个扩展类(cdef class),你需要提供一个.pxd文件(也叫做定义文件),这个文件里要声明所有的C类、属性和方法。具体可以参考文档中的共享扩展类型部分。
以你的例子为例,你需要一个名为bendingForcesClass.pxd
的文件,这个文件里要声明你想要共享的类,以及所有的cimport、模块级变量、类型定义等等:
# 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
文件中删除:
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模块中被引入,成为一个有效的类型标识符,这样就能解决你的问题了。