Cython: Overriding the `__cinit__` function with different parameters and signature - python

I am interested in subclassing an existing Cython class (we'll call it A), which has say the following __cinit__(self, int a, int b, *argv) function signature.
My new class B would have the following __cinit__(self, int a, int c, *argv), where b is no longer required, or used.
I want something along the lines of:
cdef class A:
cdef int a
cdef int b
def __cinit__(self, int a, int b, *argv):
self.a = a
self.b = b
cdef class B(A):
cdef double c
def __cinit__(self, int a, double c, *argv):
self.a = a
self.c = c
Is there a way to do this?

No. It's not possible to do what you want.
The point of using __cinit__ rather than __init__ is that __cinit__ is always called. The way that Cython ensures this is that it takes control away from you by arranging the calls to the base classes itself.
If possible it'd be better to use __init__ instead of __cinit__ unless you need to "guarantee to be called" since this gives more flexibility.
Your best option is probably to use a staticmethod factory function for class B. But you do lose the guaranteed call of B.__cinit__.

Related

Passing a struct* from one Cython class to another

I am trying to pass a struct pointer from one Cython class to another. Here is some example code:
cdef struct MyStruct:
int a
int b
cdef class MyClass:
cdef MyStruct* s
def __init__(self):
self.s = <MyStruct*> malloc(sizeof(MyStruct))
self.s.a = 1
self.s.b = 2
cdef MyStruct* get_my_struct(self):
return self.s
cdef class PrinterClass:
cdef object m
def __init__(self):
self.m = MyClass()
cpdef print_struct(self):
cdef MyStruct* my_struct
my_struct = self.m.get_my_struct()
print(my_struct.a)
When I try to compile this class, I get these 2 errors around the my_struct = self.m.get_my_struct() line:
Cannot convert Python object to 'MyStruct *
and
Storing unsafe C derivative of temporary Python reference
Why is Cython attempting to do conversions here? Can't it just pass the pointer as is?
In PrinterClass, replace cdef object m with cdef MyClass m or explicitly cast self.m to MyClass: my_struct = (<MyClass>self.m).get_my_struct(). (In addition, a __dealloc__ should be added to MyClass).
I guess the difference lies in that object is a python object(in essence,dict), while cdef class is another kind of class(in essence, struct), see Extension types (aka. cdef classes).
Expect further revelations from other experts :)

How to structure a cython module with many identical function signatures

I've written a Cython module which wraps a foreign C function, and it's working as expected. However, I'd like to wrap the rest of the functions provided by my C binary, which have identical signatures. In Python, I could just do:
def boilerplate(func):
def wrapped_f(c, d):
# modify x and y, producing mod_c and mod_d
result = func(mod_c, mod_d)
# modify foreign function return values, producing final_c, final_d
return final_c, final_d
return wrapped_f
#boilerplate
def func_a(a, b)
return _foreign_func_a(a, b)
#boilerplate
def func_b(a, b)
return _foreign_func_b(a, b)
Is there a similar pattern I can use in Cython, in order to "cythonise" wrapped_f, assuming _foreign_func_a and its accompanying structs etc. have been cimported?
However, when I move the generic operations into the decorator:
def boilerplate(func):
def wrapped(double[::1] wlon, double[::1] wlat):
cdef _FFIArray x_ffi, y_ffi
x_ffi.data = <void*>&wlon[0]
x_ffi.len = wlon.shape[0]
y_ffi.data = <void*>&wlat[0]
y_ffi.len = wlat.shape[0]
cdef _Result_Tuple result = func(x_ffi, y_ffi)
cdef double* eastings_ptr = <double*>(result.e.data)
cdef double* northings_ptr = <double*>(result.n.data)
cdef double[::1] e = <double[:result.e.len:1]>eastings_ptr
cdef double[::1] n = <double[:result.n.len:1]>northings_ptr
e_numpy = np.copy(e)
n_numpy = np.copy(n)
drop_float_array(result.e, result.n)
return e_numpy, n_numpy
return wrapped
#boilerplate
def convert_bng(double[::1] lons, double[::1] lats):
"""wrapper around threaded conversion function
"""
return convert_to_bng_threaded(lons, lats)
I get errors when
trying to convert x_ffi and y_ffi to _FFIArray to Python objects in wrapped
converting Python object func to _Result_Tuple in wrapped
converting lons and lats to _FFI_Array in convert_to_bng_threaded, and
converting _Result_Tuple back to a Python object in convert_bng_threaded
Your essential problem (based on your updated question) is that you're trying to wrap a function that takes pure C data types (and thus can only be defined as a cdef function, and can be called from Cython but not Python). However, decorators work on Python functions, so it doesn't quite come together.
Fortunately you can do something very similar handling the wrapped function a using C function pointer. You need a slightly different syntax but the idea is very much the same. (For the sake of this answer I'm assuming you are using the definitions of C data types from this previous question, which I think is reasonable)
# pass a function pointer in
cdef boilerplate(_Result_Tuple (*func)(_FFIArray, _FFIArray)):
def wrapped(double[::1] wlon, double[::1] wlat):
cdef _FFIArray x_ffi, y_ffi
x_ffi.data = <void*>&wlon[0]
x_ffi.len = wlon.shape[0]
y_ffi.data = <void*>&wlat[0]
y_ffi.len = wlat.shape[0]
cdef _Result_Tuple result = func(x_ffi, y_ffi)
cdef double* eastings_ptr = <double*>(result.e.data)
cdef double* northings_ptr = <double*>(result.n.data)
cdef double[::1] e = <double[:result.e.len:1]>eastings_ptr
cdef double[::1] n = <double[:result.n.len:1]>northings_ptr
e_numpy = np.copy(e)
n_numpy = np.copy(n)
drop_float_array(result.e, result.n)
return e_numpy, n_numpy
return wrapped
# do this instead of using a decorator syntax
convert_bng = boilerplate(&convert_to_bng_threaded)

Passing a function as a class attribute of a Cython class

I'm trying to define a Cython class that accepts a function as one of the class attribute in the __init__ function.
I followed this question and tried the following
ctypedef int (*f_type)(int, int)
cdef class ClassWFunction:
cdef f_type f
def __init__( self, f_type f):
self.f = f
def doStuff(self, int x, int y):
return self.f(x, y)
cdef int example_f(int a, int b):
return a*b
classInstance = ClassWFunction(example_f)
classInstance.doStuff(2, 4)
which gives me the error:
Cannot convert 'int (int, int)' to Python object
I also read this page from Cython's documentation and tried following that approach:
cdef class f_type:
cpdef int evaluate(self, int a, int b) except *:
return 0
cdef class ClassWFunctionV2:
cdef f_type f
def __init__( self, f_type f):
self.f = f
def doStuff(self, int a, int b):
return self.f.evaluate(a, b)
cdef class example_f(f_type):
cpdef int evaluate(self, int a, int b) except *:
return a*b
classInstance = ClassWFunctionV2(example_f)
classInstance.doStuff(3, 4)
which gives me a TypeError:
TypeError: Argument 'f' has incorrect type (expected _cython_magic_9f51dc40b83b28506fce9fb260a84618.f_type, got type)
Hopefully there's a way to make my first attempt work, since this second approach has a lot of boilerplate I don't quite understand!
Thanks for your time...
-------- Edit ----------
The point of writing the code this way is twofold:
1) to have a flexible way to change the f function when instantiating the class.
2) The f function gets reused in many of the class methods, so I'd like to assign it to self
In my pure python code, I do stuff like
def f1(x): return x**2
def f2(x): return x**3
cl1 = ClassWFunction(f1)
cl2 = ClassWFunction(f2)
and then proceed to do things with those classes. However, I'm not 100% sure this is the best way to go around, so feel free to suggest a different approach.
-------- Edit2 ----------
As a less flexible but (hopefully!) easier alternative, I tried to hard-code the function onto the class. This would fit objective (2) above, even if it doesn't fit objective (1).
Consider (this also gives an error)
ctypedef int (*f_type)(int)
cdef class ClassWFunction:
cdef f_type example_f
cdef double x
def __init__( self, int x):
self.x = x
# Tried with a python function too
cdef int example_f(int z):
return x*z
self.example_f = example_f
def doStuff(self, int x, int y):
return self.example_f(x)*y
classInstance = ClassWFunction(3)
classInstance.doStuff(2, 4)

Passing memoryview to C function

I have a C function declared as follows:
void getIndexOfState(long *p, long C, long G, long B, long *state);
Nowadays my cython wrapper code uses the buffer syntax from numpy array:
cpdef int getIndexOfState(self, np.ndarray[np.int_t, ndim=1, mode="c"] s):
cdef long out
getIndexOfState(&out, self.C, self.G, self.B, <long*> s.data)
return out
I want to use the new memoryview syntax, my question is, how can I pass the pointer to the data when using a memoryview?
I tried:
cpdef int getIndexOfState(self, long[:] s):
cdef long out
getIndexOfState(&out, self.C, self.G, self.B, s)
return out
which raised the "Cannot assign type 'long[:]' to 'long *'" error when I was trying to compile the module. There is any way to pass that pointer without coercing the memoryview back to a numpy array before calling the C function?
If the underlying data is properly contiguous/strided and there is at least one element in the memory, then it should suffice to pass a pointer to the first element (and maybe the length):
getIndexOfState(&out, self.C, self.G, self.B, &s[0])
EDIT:
One way to ensure "properly contiguous" should be the addition of "[::1]".
cpdef int getIndexOfState(self, long[::1] s):
cdef long out
getIndexOfState(&out, self.C, self.G, self.B, &s[0])
return out

Is there any type for function in Cython?

Is there any way to tell Cython compiler that param is function. Something like
cpdef float calc_class_re(list data, func callback)
Should be self-explanatory..? :)
# Define a new type for a function-type that accepts an integer and
# a string, returning an integer.
ctypedef int (*f_type)(int, str)
# Extern a function of that type from foo.h
cdef extern from "foo.h":
int do_this(int, str)
# Passing this function will not work.
cpdef int do_that(int a, str b):
return 0
# However, this will work.
cdef int do_stuff(int a, str b):
return 0
# This functio uses a function of that type. Note that it cannot be a
# cpdef function because the function-type is not available from Python.
cdef void foo(f_type f):
print f(0, "bar")
# Works:
foo(do_this) # the externed function
foo(do_stuff) # the cdef function
# Error:
# Cannot assign type 'int (int, str, int __pyx_skip_dispatch)' to 'f_type'
foo(do_that) # the cpdef function

Categories

Resources