5

Let's look at some very simple behaviour of numpy arrays:

import numpy as np
arr = np.array([1,2,3,4,5])
max_value = arr.max() # returns 5

Now I create a class which inherits from np.ndarray:

class CustomArray(np.ndarray):
    def __new__(cls, *args, **kwargs):
        obj = np.array(*args, **kwargs).view(cls)
        return obj
new_arr = CustomArray([1,2,3,4,5])

I haven't actually changed any behaviour - I just made a nominal change in what class the object is.

And yet:

new_max_value = new_arr.max() # returns CustomArray(5)

The return value is an CustomArray instance? Why? arr.max() didn't return a np.ndarray instance, just a plain numpy integer.

Similarly, why do both arr == new_arr and new_arr == arr return CustomArray instances? Shouldn't the former call arr.__eq__(new_arr), which should return a np.ndarray instance?

EDIT:

Note that I override the __new__ method for the sake of easy constructors. E.g. the equivalent of np.array([1,2,3,4,5]) can just be CustomArray([1,2,3,4,5]), whereas if I plainly inherit from np.ndarray I'd have to do something like new_arr = CustomArray((5,)); new_arr[:] = np.array([1,2,3,4,5]).

2
  • Check how subclasses like matrix and masked array handle this issue. Many methods/functions make sure the return value has the same subclass as the inputs. Note the max(axis=...) should be some sort of array. It's the scalar case the requires special handling. Commented Oct 4, 2016 at 17:22
  • I get that the scalar case is the special case, but that still leaves open the question of how my custom class, which doesn't seem to change any behaviour, still has different behaviour. Commented Oct 5, 2016 at 7:29

1 Answer 1

3

Following the numpy docs: array_wrap gets called at the end of numpy ufuncs and other numpy functions, to allow a subclass to set the type of the return value and update attributes and metadata.

class CustomArray(np.ndarray):
    def __new__(cls, a, dtype=None, order=None):
        obj = np.asarray(a, dtype, order).view(cls)
        return obj

    def __array_wrap__(self, out_arr, context=None):
        return np.ndarray.__array_wrap__(self, out_arr, context)

c = CustomArray([1,2,3,4])
c.max() # returns 4
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.