[Pyrex] cdef'd classes initialization

Daniele Varrazzo daniele.varrazzo at gmail.com
Thu Aug 10 12:36:42 UTC 2006


Lenard Lindstrom wrote:
> On 8 Aug 2006 at 18:33, Daniele Varrazzo wrote:
> 
>> Hello,
>>
>> i wrote some Pyrex classes to wrap C structures. Usually such classes
>> are responsible for the wrapped structure, and properly allocate them on
>> the heap and initialize on __init__ and free on __dealloc__. This is the
>> normal behaviour for all the instances directly created by a Python program.
>>
> First, it is better to use __new__ instead of __init__ for C members.

I stumbled against the Pyrex __new__ peculiar behavior: all the 
superclasses' __new__ get called and no communication is available in 
the calls chain.

For example, in my library each subclass of a base class must allocate 
some heap space: different subclasses require different amount of space.

Should the space be allocated by the base class __new__? I think it 
should, but how can a subclass tell the base class how much to allocate? 
__new__'s parameters can't be altered.

I have been thinking to use a virtual class getSize() to be called by 
the base class __new__(), but a test showed me it isn't possible:

     cdef class TestA:
         cdef public int num

         def __new__(self):
             self.other()

         cdef void other(self):
             self.num = 10

     cdef class TestB(TestA):
         cdef void other(self):
             self.num = 20

>>> from testinh import TestA, TestB
>>> TestA().num, TestB().num
(10, 10)

Which can be read "In __new__ method, virtual methods are not virtual",
just as in C++ constructors. This is not the case for the __init__()
method, where the test result would have been (10, 20). Probably it
should be specified in the documentation. Or is it a bug? If it were 
possible to keep the virtual behavior of methods, __new__ would gain a 
lot of extra flexibility.

The heap space can't either be allocated by some subclass: it wouldn't 
be possible to subclass it again by a class requiring more space: the 
__new__() chain would allocate two chunks.

Maybe i could implement all the classes except their __new__ in a tree 
of private classes, and provide a matching set of "final" classes, each 
one  implementing a __new__ and not to be subclassed anymore. But i feel 
it a little baroque (with mixin classes it would be more elegant, but no 
way without the can of worm of multiple inheritance), so i preferred to 
use the __init__, which provided the flexibility i needed.

> You can use a root class for the case of no allocation/deallocation, 
> and a derived class to export to Python. Here is an example. It also 
> shows how to access C members/methods from a factory function.
 >
> cdef class CRoot:
>     """For factory function"""
>     cdef int datum
> 
>     def get_datum(self):
>         return self.datum
> 
> def make_object(int datum):
>     cdef CRoot o
>     o = CRoot()
>     o.datum = datum
>     return o
> 
> cdef class C(CRoot):
>     """Has allocator/deallocator"""
>     def __new__(self, int datum):
>         self.datum = datum
> 
>     def __dealloc__(self):
>         pass

This is not the first time i am faced to the need to hide an extension
type to be directly exposed to Python: in your example would it be
possible to hide CRoot from the module dict?





More information about the Pyrex mailing list