Description
I'm running into a few unexpected behaviors when subclassing ndarray
, as described here: (http://docs.scipy.org/doc/numpy/user/basics.subclassing.html).
Here's a simple subclass that doesn't try to add any functionality:
import numpy as np
class MyArray(np.ndarray):
def __new__(cls, input_array, info=None):
# cast to be our class type
obj = np.asarray(input_array).view(cls)
return obj
def __array_finalize__(self, obj):
pass
Notice that the behavior of .sum()
differs between an ndarray
and MyArray
:
>>> np.array([1,2,3]).sum()
6
>>> MyArray([1,2,3]).sum()
MyArray(6)
I'd like my subclass to have the np.array
behavior -- I don't want MyArray.sum()
to return 0-dimensional arrays.
Before I try to solve this problem, I want to point out that the difference is also there for the object dtype
. Here's a pretty useless object definition, that supports addition:
class SelfishObject(object):
def __add__(self, other):
return self
def __radd__(self, other):
return self + other
Notice that the behavior of .sum()
still differs in terms of return type:
>>> np.array([SelfishObject(), SelfishObject()]).sum()
<__main__.SelfishObject object at 0x1032e25d0>
>>> MyArray([SelfishObject(), SelfishObject()]).sum()
MyArray(<__main__.SelfishObject object at 0x1032e2610>, dtype=object)
It seems I can force the behavior I want, using __array_wrap__
:
class MyArray(np.ndarray):
def __new__(cls, input_array):
# cast to be our class type
obj = np.asarray(input_array).view(cls)
return obj
def __array_finalize__(self, obj):
pass
def __array_wrap__(self, out_arr, context=None):
if out_arr.ndim:
return np.ndarray.__array_wrap__(self, out_arr, context)
>>> np.array([SelfishObject(), SelfishObject()]).sum()
<__main__.SelfishObject object at 0x1032e2610>
>>> MyArray([SelfishObject(), SelfishObject()]).sum()
<__main__.SelfishObject object at 0x1032e2590>
>>> np.array([1,2,3]).sum()
6
>>> MyArray([1,2,3]).sum()
6
... but I'm wondering whether this is the preferred/recommended way to deal with this, and whether guidelines for this issue should be added to the documentation.
One related potential unexpected behavior is that the exceptions raised in __array_wrap__
seem to get caught at a higher level, and therefore appear to be ignored. Even odder, raising in __array_wrap__
also solves the 0-dimensional array problem:
class MyArray(np.ndarray):
def __new__(cls, input_array):
# cast to be our class type
obj = np.asarray(input_array).view(cls)
return obj
def __array_finalize__(self, obj):
pass
def __array_wrap__(self, out_arr, context=None):
raise RuntimeWarning
>>> MyArray([SelfishObject(), SelfishObject()]).sum()
<__main__.SelfishObject object at 0x1032e2590>
>>> MyArray([1,2,3]).sum()
6