96

Is there a simple way to create an immutable NumPy array?

If one has to derive a class from ndarray to do this, what's the minimum set of methods that one has to override to achieve immutability?

NPE
  • 486,780
  • 108
  • 951
  • 1,012

3 Answers3

143

You can make a numpy array unwriteable:

a = np.arange(10)
a.flags.writeable = False
a[0] = 1
# Gives: ValueError: assignment destination is read-only

Also see the discussion in this thread:

http://mail.scipy.org/pipermail/numpy-discussion/2008-December/039274.html

and the documentation:

http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.flags.html

fqq
  • 103
  • 5
JoshAdel
  • 66,734
  • 27
  • 141
  • 140
  • 26
    Alternatively, `a.setflags(write=False)`. – lafras Apr 04 '11 at 18:46
  • 1
    @lafrasu Which would you say is the preferred form, `setflags()` or `flags.writeable=`? – NPE Apr 05 '11 at 08:52
  • 4
    @aix: A quick look at the documentation would make it seem as if the two approaches are identical. Personally, I prefer using a method to set attributes. – lafras Apr 05 '11 at 10:47
  • 3
    Does this also make it memoizable? – endolith Aug 02 '19 at 15:03
  • 22
    IMPORTANT!! Numpy DOES NOT have an immutable array. Arrays with `.flags.writeable = False` are still not immutable. If `x` is an array, `y = x[:]; x.flags.writeable = False; y[0] = 5` updates the first element of `x` to `5`. – James Parker May 26 '20 at 12:27
  • @JamesParker you do get an error if you set the writeable flag before creating `y`. – asmeurer Jul 23 '20 at 18:13
  • 2
    @JamesParker It seems the flags come with the array when you slice. So if the `writable` flag is set to `False` *before* you slice then then `y` above would fail on update. – Damon Maria Jul 24 '20 at 20:12
  • @DamonMaria I tested it and that seems to be correct. You can only change `y.flags.writeable = True` when that value for `x` is true. It's just changing `writeable` for `x` does not update that of `y` directly. – James Parker Jul 26 '20 at 10:41
  • @James Parker apparently the order is important. A slice before setting writable to False, is not blocked. “...locking a base object does not lock any views that already reference it”. See under `writable` tag https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flags.html – ChrisFreeman May 29 '21 at 01:26
  • FYI, I used ```arr.flags.writeable = False```, but then I called ```for i in range(5): np.random.shuffle(arr[:,i])```, which shuffled the array in place - no errors or warnings. – Joe Dec 17 '21 at 14:27
  • @Joe strange, I just tried it and I get an error... – Vinzent Jan 07 '23 at 01:47
  • @Vinzent, sorry, my comment was missing an important detail - I used `arr = np.arange(30).reshape(6,5)`. If you tried `for i in range(5): np.random.shuffle(arr[:,i])` on `arr = np.arange(10)` you would definitely get an `IndexError`. – Joe Feb 04 '23 at 14:45
0

I have a subclass of Array at this gist: https://gist.github.com/sfaleron/9791418d7023a9985bb803170c5d93d8

It makes a copy of its argument and marks that as read-only, so you should only be able to shoot yourself in the foot if you are very deliberate about it. My immediate need was for it to be hashable, so I could use them in sets, so that works too. It isn't a lot of code, but about 70% of the lines are for testing, so I won't post it directly.

Note that it's not a drop-in replacement; it won't accept any keyword args like a normal Array constructor. Instances will behave like Arrays, though.

sfaleron
  • 61
  • 1
  • 3
0

Setting the flag directly didn't work for me, but using ndarray.setflags did work:

a = np.arange(10)
a.setflags(write=False)
a[0] = 1  # ValueError
Kris
  • 22,079
  • 3
  • 30
  • 35