Using Cython To Early Type Class Attributes
Solution 1:
What you want is to define an extension type. In particular your code should look like:
import numpy as np
cimport numpy as np
cdefclass MyClass:
cdefdouble var1
cdefnp.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 classMyClass:
cdef double var1
cdef np.ndarray[double, ndim=1] Redges
^
------------------------------------------------------------
test_cython.pyx:6:36: Buffer types only allowed asfunction 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
cdefclass MyClass:
cdefdouble var1
#cdef np.ndarray[double, ndim=1] Redgesdef__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
cdefclass MyClass:
cdefdouble var1
cdefobject 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
cdefclass MyClass:
cdefdouble var1
cdefobject Redges
def__init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
defdo_stuff(self):
cdefnp.ndarray[double, ndim=1] ar
ar = self.Redges
ar[0] += 1return ar[0]
In this way inside the do_stuff
function you can have all the speed of cython using ar
.
Solution 2:
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
cdefclass MyClass:
cdefpublic double var1
cdefpublic np.float64_t[:] Redges
def__init__( self, np.ndarray[double, ndim=1] Redges ):
self.Redges = Redges
With that approach do_stuff
gets simpler:
defdo_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)
Post a Comment for "Using Cython To Early Type Class Attributes"