Code block 1 using __init__
%%cython -3
cdef class c:
cdef:
int a
str s
def __init__(self):
self.a=1
self.s="abc"
def get_vals(self):
return self.a,self.s
m=c()
print(m.get_vals())
Code block 2 using __cinit__
%%cython -3
cdef class c:
cdef:
int a
str s
def __cinit__(self): # cinit here
self.a=1
self.s="abc"
def get_vals(self):
return self.a,self.s
m=c()
print(m.get_vals())
I tested both of these codes, and both run without error.
In this case, what's the point of using __cinit__
instead of __init__
?
I've read the official article, I got confused by one sentence:
If you need to pass a modified argument list to the base type, you will have to do the relevant part of the initialization in the
__init__()
method instead, where the normal rules for calling inherited methods apply.
What does "modified argument" mean? Here, why should I use init rather than cinit?
It's mainly about inheritance. Suppose I inherit from your class C
:
class D(C):
def __init__(self):
pass # oops forgot to call C.__init__
class E(C):
def __init__(self):
super().__init__(self)
super().__init__(self) # called it twice
How __init__
ends up being called is entirely up to the classes that inherit from it. Bear in mind there may be multiple layers of inheritance.
Additionally, a fairly common pattern for creating classes that wrap C/C++ objects is to create a staticmethod
cdef
function as an alternative constructor:
cdef class C:
def __cinit__(self):
print("In __cinit__")
@staticmethod
cdef make_from_ptr(void* x):
val = C.__new__(C)
# do something with pointer
return val
In this case again, __init__
typically isn't called.
In contrast __cinit__
is guaranteed to be called exactly once and this happens automatically by Cython at an early stage of the process. This is most important when you have cdef
attributes (such as C pointers) that your class relies on being initialized. It would be impossible for a Python derived class to even set these up, but __cinit__
can ensure that they are.
In your case it probably doesn't matter - use whichever you're happy with.
In terms of "modified arguments" it's saying you can't replicate this with __cinit__
:
class NameValue:
def __init__(self, name, value):
self.name = name
self.value = value
class NamedHelloPlus1(NamedValue):
def __init__(self, value):
super().__init__("Hello", value+1)
i.e. NamedHelloPlus1
controls what arguments NamedValue
gets. With __cinit__
Cython all the calls to __cinit__
receive exactly the same arguments (because Cython arranges the call - you cannot call it manually).
cinit should be used where C level initialization of object is required. Please be careful here, they may not be a fully valid python object yet. However, anything that cannot be done in cinit, needs to happen in init. By this time, all objects are valid python objects.
if I understand it correctly, it points to modifiable arguments in derived class. Arguments list may be modified or parsed differently that the base type init. In such cases init needs to be used. This may be useful in giving you an insight on what I am trying to explain
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With