[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