1

The following code

def change(in_array):

    in_array += 1             # line a

    in_array = in_array + 1   # line b


x = np.array([1, 2, 3])
change(x)
print(x)

prints [2 3 4]. Why is this so (why are the two lines a and b different)? Could you please suggest other ways of modifying NumPy array as is done in line a? One way would be in_array[:] = in_array + 1; would the values of the passed in array always be changed in case of in_array[:] = ...?

kevin811
  • 31
  • 7

1 Answers1

1

Let's see next code:

Try it online!

import numpy as np

def change(in_array, variant = 0):
    if variant == 0:
        in_array += 1             # line a
    elif variant == 1:
        in_array = in_array + 1   # line b
    elif variant == 2:
        in_array[:] = in_array + 1
    elif variant == 3:
        np.put(in_array, np.arange(in_array.size), in_array + 1)
    elif variant == 4:
        np.copyto(in_array, in_array + 1)
    elif variant == 5:
        np.place(in_array, np.ones_like(in_array), in_array + 1)
    else:
        assert False, variant
    print('after, inside', in_array)

orig = np.array([1, 2, 3])
for variant in [0, 1, 2, 3, 4, 5]:
    print('variant', variant)
    x = np.copy(orig)
    print('before', x)
    change(x, variant)
    print('after, outside', x, ('changed', 'not changed')[bool(np.all(x == orig))])

It outputs:

variant 0
before [1 2 3]
after, inside [2 3 4]
after, outside [2 3 4] changed
variant 1
before [1 2 3]
after, inside [2 3 4]
after, outside [1 2 3] not changed
variant 2
before [1 2 3]
after, inside [2 3 4]
after, outside [2 3 4] changed
variant 3
before [1 2 3]
after, inside [2 3 4]
after, outside [2 3 4] changed
variant 4
before [1 2 3]
after, inside [2 3 4]
after, outside [2 3 4] changed
variant 5
before [1 2 3]
after, inside [2 3 4]
after, outside [2 3 4] changed

You may see that variant 0 gives correct after answer inside and outside. While variant 1 gives correct only inside.

This is because variant 0 modifies actually given array that was provided in function. While variant 1 creates a modified copy inside function which stays only inside function, the actual passed argument is not modified.

So variant 0 modifies passed argument, while variant 1 modifies a copy of passed argument.

In Python code a = a + 1 inside a function actually creates o a copy of variable a local in function. And this local variable stays only until function end and is deleted at the end of running function.

Variant 2, a[:] creates view of original array, it is like a reference to subset of elements of original array, just : is view to the whole array, assigning to this view modifies original array. Modifiable view is also created by slices like a[1:3] and indexing like a[2] or a[[1,2]].

Writing a[:] = some_operation(a) is probably the most intuitive and fastest way of assigning back to a of modified/transformed copy of a.

Variants 3, 4, 5 are other fancy ways of assigning right-hand-side expression back to array a.

Arty
  • 14,883
  • 6
  • 36
  • 69