使用cython早期键入类属性



我正在编写一个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这样的缓冲区需要具有可变大小的structs。

如果您想要类型为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)

最新更新