[Numpy-discussion] subclassing ndaray

Colin J. Williams cjw at sympatico.ca
Sat Feb 25 05:18:07 EST 2006

Travis Oliphant wrote:

> Colin J. Williams wrote:
>> I have a subclass Bar, a 1-dim array which has some methods and some 
>> attributes.  One of the attributes is a view of the Bar to permit 
>> different shaping.
> The ndarray handles sub-classing a little-bit differently.   All array 
> constructors go through the same C-code call which can create 
> sub-classes as well.    (it's what gets called by ndarray.__new__).
> If there is a parent object, then additionally, the
> __array_finalize__(self, parent)
> method is called right-after creation of the sub-class.   This is 
> where attributes should be finalized.  But, care must be taken in this 
> code so that a recursion is not setup
> If this mechanism is not sufficient for you, then you need to use a 
> container class (for this reason UserArray has been resurrected to 
> serve as a default container class model---it needs more testing, 
> however).
> The problem __array_finalize__ helps fix is how to get subclasses to 
> work well without having to re-define every single special method like 
> UserArray does.
> For the most part it seems to work, but I suppose it creates a few 
> surprises if you are not aware of what is going on.
> The most important thing to remember is that attributes are not 
> automatically carried over to new instances because new instances can 
> be created without every calling __new__ or __init__.
> I'm sure this mechanism can be improved upon and I welcome suggestions.

Thanks for this.

Does this mean that whenever we subclass ndarray, we should use 
__array_finalize__ (with its additional 'parent' parameter) instead of 
Python's usual __init__?

It would help if you could clarify the role of 'parent'.

    [Dbg]>>> h(self.__array_finalize__)
    Help on built-in function __array_finalize__:


Is parent the next type up in the type hierarchy?  If so, can this not 
be determined from self.__class__?

I've tried a similar operation with the Python library's sets.Set.  
There, __init__ is called, ensuring that the expression is of the 
appropriate sub-type.

>> Suppose that 'a' is an instance of 'Bar', which has a method 'show' 
>> and a view attribute 'v'.
>> a ^ 15 returns a Bar instance, with its methods but without the 
>> attributes.
>> I am attempt to change this, Bar has a method __xor__, see below:
>>      def __xor__(self, other):
>>        ''' Exclusive or: __xor__(x, y) => x ^ y . '''
>>        z=
>> 1                                                                    
>>    <<  this loops to the recursion limit
>>        result= ArrayType.__xor__(self, other)
>>        n= self.n
>>        result.n= n
>>        result.rowSize= self.rowSize
>>        result.show= self.show
>>        result.v= _n.reshape(result.view(), (n*n, n*n))
>>        return result
> Look at the __array_finalize__ method in defmatrix.py for ideas about 
> how it can be used.

        def __array_finalize__(self, obj):
            ndim = self.ndim
            if ndim == 0:
                self.shape = (1,1)
            elif ndim == 1:
                self.shape = (1,self.shape[0])

These are functions for which one would use __init__  in numarray.  This 
doesn't describe or illustrate the role or purpose of the parent object.

Colin W.

More information about the NumPy-Discussion mailing list