Search code examples
arraysvariablesnumpyglobalinternal

numpy array, h[:]=h+1 vs. h=h+1


Seems like h[:]=h+1 and h=h+1 all add 1 to all elements in h.

But difference may be found here below, can you explain why?? Looks like in the second case, h is not only changing as an function internal variable, but also acting like overriding the global variable h...This is so confusing... Thank you for any input!

import numpy as np

def fun(h):
    h=h+1
    return h

h=np.zeros((2,2))+1
hI=h.copy()

for i in range(0,3):          
    hI = fun(h) 

print h
print hI
[[ 1.  1.]
 [ 1.  1.]]
[[ 2.  2.]
 [ 2.  2.]]

Whereas

import numpy as np

def fun(h):
    h[:]=h+1
    return h

h=np.zeros((2,2))+1
hI=h.copy()

for i in range(0,3):          
    hI = fun(h) 

print h
print hI

[[ 4.  4.]
 [ 4.  4.]]
[[ 4.  4.]
 [ 4.  4.]]

Solution

  • This is not surprising. I think you are being confused by the fact that you happen to have named your variable h in use of the function fun. But the name of that variable doesn't matter, and it is not reaching up to the parent scope to modify the variable h ... rather, the argument passed to fun is a name for something, not a copy of it. So mutations internal to fun still affect the object being named, (in this case, that object is the global scoped h array).

    To show this more clearly, consider the case below, where all I did was rename the variable inside of fun to be z instead of h ... yet the same result happens.

    In [14]: def fun(z):
       ....:     z[:] = z + 1
       ....:     return z
       ....: 
    
    In [15]: h = np.zeros((2,2)) + 1
    
    In [16]: for i in range(0, 3):
       ....:     hI = fun(h)
       ....:     
    
    In [17]: print h
    [[ 4.  4.]
     [ 4.  4.]]
    
    In [18]: print hI
    [[ 4.  4.]
     [ 4.  4.]]
    

    In the first example, the internal variable is not mutated. Assigning

    h = h + 1
    

    causes the local h (the h inside the function scope) to be a new name, a name for the object created from the operation h + 1 for whatever thing was previously named by h in that function scope (e.g. the argument that was passed in).

    Since the broadcasting operation h + 1 for a NumPy array h results in a new array, the statement h = h + 1 is not mutating h ... merely causing the name h to refer to something new (h + 1).

    Whereas in the second example,

    h[:] = h + 1
    

    is to say that the contents of whatever object is named by h are updated.

    You could call it z[:] as I did, it doesn't matter. The "thing[:]" on the left hand side is notation for slice assignment, which is implemented for NumPy arrays as a mutation of the sliced object's data.