Is this a correct way to create a read-only view of a numpy array?

Viewed 927

I would like to create a read-only reference to a NumPy array. Is this a correct way to make b a read-only reference to a (a is any NumPy array)?

def get_readonly_view(a):
    b = a.view()
    b.flags.writeable = False
    return b

Specifically, I would like to ensure the above does not 'copy' the contents of a? (I tried testing this with np.shares_memory and it does return True. But I am not sure if that is a correct test.)

In addition I wonder if get_readonly_view is already implemented in NumPy?


Update. It has been suggested to turn the array to a class property to make it read-only. I think this does not work:

import numpy as np

class Foo:

    def __init__(self):
        self._a = np.arange(15).reshape((3, 5))


    @property
    def a(self):
        return self._a


    def bar(self):
        print(self._a)

But the client can change contents of _a:

>> baz = Foo()
>> baz.bar()
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]
>> baz.a[1, 2] = 10
>> baz.bar()
[[ 0  1  2  3  4]
 [ 5  6 10  8  9]
 [10 11 12 13 14]]

while I would like baz.a[1, 2] = 10 to raise an exception.

1 Answers

Your approach seems to be the suggested way of creating a read-only view.

In particular, arr.view() (which can also be written as a slicing arr[:]) will create a reference to arr, while modifying the writeable flag is the suggested way of making a NumPy array read-only.

The documentation also provides some additional information on the inheritance of the writeable property:

The data area can be written to. Setting this to False locks the data, making it read-only. A view (slice, etc.) inherits WRITEABLE from its base array at creation time, but a view of a writeable array may be subsequently locked while the base array remains writeable. (The opposite is not true, in that a view of a locked array may not be made writeable. However, currently, locking a base object does not lock any views that already reference it, so under that circumstance it is possible to alter the contents of a locked array via a previously created writeable view onto it.) Attempting to change a non-writeable array raises a RuntimeError exception.

Just to reiterate and inspect what is going on:

import numpy as np


def get_readonly_view(arr):
    result = arr.view()
    result.flags.writeable = False
    return result


a = np.zeros((2, 2))
b = get_readonly_view(a)

print(a.flags)
#   C_CONTIGUOUS : True
#   F_CONTIGUOUS : False
#   OWNDATA : True
#   WRITEABLE : True
#   ALIGNED : True
#   WRITEBACKIFCOPY : False
#   UPDATEIFCOPY : False
print(b.flags)
#   C_CONTIGUOUS : True
#   F_CONTIGUOUS : False
#   OWNDATA : False
#   WRITEABLE : False
#   ALIGNED : True
#   WRITEBACKIFCOPY : False
#   UPDATEIFCOPY : False

print(a.base)
# None
print(b.base)
# [[0. 0.]
#  [0. 0.]]

a[1, 1] = 1.0
# ...works
b[0, 0] = 1.0
# raises  ValueError: assignment destination is read-only
Related