-
-
Notifications
You must be signed in to change notification settings - Fork 11.9k
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):
passNotice 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 + otherNotice 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