[Numpy-discussion] Multiple inheritance from ndarray

Travis Oliphant oliphant.travis at ieee.org
Tue Mar 21 10:03:05 CST 2006

Robert Lupton wrote:
> I've finally found time to return to this problem.  Travis's made the
> suggestion that I could use code along the lines of:
>> class Image(ndarray, actImage):
>>    def __new__(subtype, *args)
>>        act1 = actImage.__new__(actImage, *args)
>>        actImage.__init__(act1, *args)
>>        arr = array(act1.getArray(), 'd', copy=False)
>>        self = arr.view(subtype)
>>        return self
> and this makes sense.  Unfortunately, the arr.view(subtype) fails:
>     TypeError: Cannot change descriptor for objectarray.
> [incidently, is that a typo for "object array"; the string's built
> by "C string" "concatenation").
Yes, I think so.  Hmm, it looks like you have object arrays floating 
around which seems strange.

But, as I thought more about multiple inheritance I realized that there 
really is a case for a container class like UserArray that you can 
inherit from that only "wraps" the ndarray and does not try to inherit 
from it. 

The analogy I see with C-code is that a container class has a pointer to 
an array object structure, while a sub-class has the arrayobject 
structure itself as part of the new subclass. 

So, I added back the UserArray to numpy.  Now, you can inherit from 
numpy.lib.UserArray.UserArray just like before.

> Ideas?
> The C type "actImage" looks like:
> typedef struct actImage {
>     int nrow;                 // number of rows
>     int ncol;                 // number of columns
>     void *base;                 // allocated memory, if owned by this 
> object
>     int baseRefCount;             // reference counter for base
>     double **rows;             // Fluxes for pixels
>     PyArrayObject *_array;         // pointer to Numeric array; used 
> by actNumPy
> } actImage;
It looks like you have a "container" class by default here.   You could, 
if you wanted make this a subclass by definining actImage as

typedef struct actImage {
       PyArrayObject _array;
       int nrow;  // redundant
       int ncol;    // redundant
       void * base; // also seems redundant
       int baseRefCount; // perhaps not necessary either
       double **rows; // truly new information
} actImage;

Now, a pointer to actImage could be cast to a (PyArrayObject *) and used 
wherever arrayobjects are expected and work just fine.

I'm not sure why you have a different pointer for allocated memory which 

It seems to me that you should create a sub-type in C of the ndarray 
rather than a container class. But, a container class would also 
continue to work.  With the re-addition of UserArray it would work in 
much the same way as well. 

> The _array is set in the C actImage constructor with:
>     im->_array = (PyArrayObject *)PyArray_FromDimsAndData(2, dims, 
> PyArray_DOUBLE, (void *)im->base);
For the subclass you would need to do a little more work in the 
constructor because PyArray_FromDims returns only a filled-in 
PyArrayObject structure.  You could copy all the elements of this array 
over (this wouldn't copy data just pointers), and then delete the 
PyArrayObject structure (don't use DECREF, just tp_free).   Then you 
could fill in the rest of the actImage structure.
> and getArray is a wrapper around:
> PyObject *actImageGetNumPy(const actImage *im)
> {
>     Py_XINCREF(im->_array);
>     return PyArray_Return(im->_array);
> }
You could use the new tp_members structure here which allows you to get 
access to parts of your C-structure very easily. 

The easiest transition path is to just use the re-added UserArray as 
before.   If you want to learn to sub-class, that could be done as 
well.  But, there are no sub-classes in C that I know of yet, so 
although it is possible, there may be some subtle issues uncovered in 
the process.


More information about the Numpy-discussion mailing list