Reputation:
I'm writing a python class and I would like to accelerate the execution using cython early typing.
I get the error "Syntax error in C variable declaration"
when I try to cython compile the following:
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
The error concerns the syntax of the last line involving self.var1
. Am I not allowed to type class attributes directly? Do I always have to break this up into two steps such as,
cdef double var1
self.var1 = var1
The full error traceback is,
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
Upvotes: 11
Views: 8082
Reputation: 4798
The answer of @bakuriu is quite good, I'd like to add how this can be done with keeping a memory view as a class member:
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
With that approach do_stuff
gets simpler:
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)
Upvotes: 3
Reputation: 101969
What you want is to define an extension type. In particular your code should look like:
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
Note that you cannot impose the type of instance attributes in a normal class
, because python allow people to change them and their types. If you try to put a cdef
at class level in a normal python class you'll receive a compiler error by Cython.
Compiling the above code raises the following error:
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
Now, this is not a syntax error. The syntax is fine. The problem is that you simply cannot have an instance attribute with np.ndarray
as type. It is a limitation of cython. In fact if you comment the cdef np.ndarray[double, ndim=1] Redges
line the file is compiled correctly:
Code:
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
Output:
$cython test_cython.pyx
$
Note: no output from cython
which means the file was compiled succesfully.
this limitation is explained in the documentation I linked above, at the section Attributes:
Attributes of an extension type are stored directly in the object’s C
struct
. [omissis]Note: You can only expose simple C types, such as ints, floats, and strings, for Python access. You can also expose Python-valued attributes.
The fact that you can only expose simple C data-types is because the attributes are members of the struct
. Allowing a buffer like an np.ndarray
would require to have variable size struct
s.
If you want an instance attribute of type np.ndarray
the best you can do is to define an attribute with a generic type of object
and assign the array to it:
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
However now everytime you access self.Redges
you lose the speed up of cython. If you access it many times you can assign it to a local variable with the correct type.
Here's what I mean:
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]
In this way inside the do_stuff
function you can have all the speed of cython using ar
.
Upvotes: 12