0

How is base determined for record arrays? The docs seem to describe the same behavior as regular arrays, but that's not what it is. Here's a simple array, and a record array created from it.

>>> arr = np.zeros(10, dtype=[('a', float), ('b', int), ('c', int)])
>>> rec = arr.view(np.recarray)

The base of the record array is correctly set

>>> arr is rec
False

>>> arr is rec.base
True

The base is correctly set for field accesses of the regular array.

>>> arr['a'].base is arr
True

However, for the record array I can't determine what the base is. It's not the regular array, the record array, None, or anything else that I've tried.

>>> rec['a'].base is arr
False

>>> rec['a'].base is rec
False

>>> rec['a'].base is None
False

>>> rec['a'].base is rec['a']
False

>>> rec['a'].base is rec['a'].base
False

>>> f = rec['a']
>>> f.base is f
False

It behaves as expected for indexing slices

>>> arr[:3].base is arr
True

>>> rec[:3].base is rec
True

And it definitely still points to the same memory

>>> arr[0]
(0., 0, 0)

>>> rec['a'] = 1

>>> arr[0]
(1., 0, 0)

So, how can the actual base of a record array be found?

user2699
  • 2,927
  • 14
  • 31
  • As far as I can tell `recarray` doesn't add much, other than the attributes style of field access. As for checking the data buffer location, I like to use `.__array_interface__['data']`. – hpaulj Sep 05 '18 at 17:11

1 Answers1

1

The "actual base" is still the base attribute. If you want to follow the base chain recursively, go ahead:

def recursive_base(arr):
    while arr.base is not None:
        arr = arr.base
    return arr

If you want to see why rec['a'].base is not rec, then take a look at recarray.__getitem__:

def __getitem__(self, indx):
    obj = super(recarray, self).__getitem__(indx)

    # copy behavior of getattr, except that here
    # we might also be returning a single element
    if isinstance(obj, ndarray):
        if obj.dtype.fields:
            obj = obj.view(type(self))
            if issubclass(obj.dtype.type, nt.void):
                return obj.view(dtype=(self.dtype.type, obj.dtype))
            return obj
        else:
            return obj.view(type=ndarray)
    else:
        # return a single element
        return obj

In your case, the returned object is a view of the result returned by ndarray.__getitem__, and the base is the object returned by ndarray.__getitem__. Generally, though, it's not guaranteed whether NumPy will flatten the base chain when setting a new array's base.

user2357112
  • 260,549
  • 28
  • 431
  • 505