我正在编写一个python类,我想使用cython早期键入来加速执行
当我尝试cython编译以下内容时,我得到错误"Syntax error in C variable declaration"
:
import numpy as np
cimport numpy as np
class MyClass:
def __init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
cdef double self.var1
该错误与涉及self.var1
的最后一行的语法有关。是否不允许我直接键入类属性?我总是要把它分成两个步骤吗?比如,
cdef double var1
self.var1 = var1
完整的错误追溯是,
test.pyx:7:24:
Syntax error in C variable declaration
Traceback (most recent call last):
File "setup.py", line 9, in <module>
ext_modules = cythonize('test.pyx'), # accepts a glob pattern
File "/usr/lib/python2.7/dist-packages/Cython/Build/Dependencies.py", line 713, in cythonize
cythonize_one(*args[1:])
File "/usr/lib/python2.7/dist-packages/Cython/Build/Dependencies.py", line 780, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: calc_iliev_sphere.pyx
您想要的是定义扩展类型。特别是你的代码应该看起来像:
import numpy as np
cimport numpy as np
cdef class MyClass:
cdef double var1
cdef np.ndarray[double, ndim=1] Redges
def __init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
请注意,不能在普通class
中强制实例属性的类型,因为python允许人们更改它们及其类型。如果您试图将cdef
放在普通python类的类级别,您将收到Cython的编译器错误。
编译上述代码会引发以下错误:
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
cimport numpy as np
cdef class MyClass:
cdef double var1
cdef np.ndarray[double, ndim=1] Redges
^
------------------------------------------------------------
test_cython.pyx:6:36: Buffer types only allowed as function local variables
现在,这不是语法错误。语法很好。问题是,您只是不能具有类型为np.ndarray
的实例属性。这是cython的局限性。事实上,如果您对cdef np.ndarray[double, ndim=1] Redges
行进行注释,则文件编译正确:
代码:
import numpy as np
cimport numpy as np
cdef class MyClass:
cdef double var1
#cdef np.ndarray[double, ndim=1] Redges
def __init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
输出:
$cython test_cython.pyx
$
注意:cython
没有输出,这意味着文件编译成功。
我在上面链接的文档"属性:"一节中解释了这个限制
扩展类型的属性直接存储在对象的C中
struct
。注意:您只能为Python访问公开简单的C类型,如int、float和string。您还可以公开Python值属性。
您只能公开简单C数据类型的事实是因为这些属性是struct
的成员。允许像np.ndarray
这样的缓冲区需要具有可变大小的struct
s。
如果您想要类型为np.ndarray
的实例属性,您可以做的最好的事情就是定义一个具有object
泛型类型的属性,并将数组分配给它:
import numpy as np
cimport numpy as np
cdef class MyClass:
cdef double var1
cdef object Redges
def __init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
但是现在每次访问self.Redges
都会失去cython的速度。如果您多次访问它,您可以将它分配给具有正确类型的局部变量。我的意思是:
import numpy as np
cimport numpy as np
cdef class MyClass:
cdef double var1
cdef object Redges
def __init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
def do_stuff(self):
cdef np.ndarray[double, ndim=1] ar
ar = self.Redges
ar[0] += 1
return ar[0]
通过这种方式,在do_stuff
函数中,您可以使用ar
获得cython的所有速度。
@bakuriu的答案很好,我想补充一下如何将内存视图作为类成员来完成:
import numpy as np
cimport numpy as np
cdef class MyClass:
cdef public double var1
cdef public np.float64_t[:] Redges
def __init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
有了这种方法,do_stuff
变得更简单:
def do_stuff(self):
# With using buffer protocol, this just wraps the memory view
# with numpy object without copying data
np_redges = np.asarray(self.Redges)
# Now you have np_redges, a numpy object. Even though, it's not a pure
# C array, it allows calling numpy functions with all the power of MKL, e.g.:
np.add(np_redges, 1.0, np_redges)