1

Could anyone explain why fun1 doesn't modify the value of the variable y, while fun2 does? I need to modify an array row by row, but updating y at the same time is not the behavior I'm looking for.

def fun1(x):
    x = 2*x
    return x


def fun2(x):
    for i in range(0, x.shape[0]):
        x[i, :] = 2*x[i, :]
    return x


y = np.random.uniform(0, 100, (10, 10))

z1 = fun1(y)
print(np.array(z1 == y).all())
# False

z2 = fun2(y)
print(np.array(z2 == y).all())
# True
5
  • 3
    Possible duplicate of Python functions call by reference Commented Jul 5, 2018 at 15:43
  • 3
    The first reassigns the variable x, thus making it local. The second modifies a mutable object. Commented Jul 5, 2018 at 15:46
  • does that passing of the variable y into the function not automatically assign it locally as x? @hpaulj Commented Jul 5, 2018 at 15:54
  • y references an ndarray object. Initially the local variable x references the same object. 2*x makes a new array. x[i,:]= modifies the original array. Commented Jul 5, 2018 at 15:59
  • My first comment was a bit off. x is a local variable in both cases. When I said 'making it local' I was thinking of a case where y is used inside the function (but not passed as an argument). In either case the distinction between reassigning a variable and modifing an object is important. Commented Jul 5, 2018 at 16:03

3 Answers 3

1

Modifying your function to show the id of the objects

def fun1(x):
    print(id(x),id(y))
    x = 2*x
    print(id(x))
    return x

In [315]: y = np.arange(3)
In [316]: id(y)
Out[316]: 140296824014768
In [317]: z = fun1(y)
140296824014768 140296824014768    
140296823720096
In [318]: id(z)
Out[318]: 140296823720096

So the array referenced by y is passed to the function, and can be referenced by both x (the argument variable) and y (the external variable). But the assignment changes the x reference - that object is passed back to z. y is unchanged.

def fun2(x):
    print(id(x), id(y))
    x[0] = 23
    print(id(x))
    return x

With this 2nd function, the assignment changes an element of x, but doesn't change the id of the referenced object. y,x and z all reference the same array.

In [320]: y
Out[320]: array([0, 1, 2])
In [321]: id(y)
Out[321]: 140296824014768
In [322]: z = fun2(y)
140296824014768 140296824014768
140296824014768
In [323]: id(z)
Out[323]: 140296824014768
In [324]: z
Out[324]: array([23,  1,  2])
In [325]: y
Out[325]: array([23,  1,  2])

If we make a copy of y, either before passing it to the function, or inside the function, then modifying x will not modify y.

In [327]: y = np.arange(3)
In [328]: id(y)
Out[328]: 140296823645328
In [329]: z = fun2(y.copy())
140296823647968 140296823645328
140296823647968
In [330]: id(z)
Out[330]: 140296823647968
In [331]: z
Out[331]: array([23,  1,  2])
In [333]: y
Out[333]: array([0, 1, 2])

The fact that we are passing the array to a function doesn't change the need for a copy. We'd get the same behavior even we just performed the action at the top level.

In [334]: y = np.arange(3)
In [335]: x = y.copy()
In [336]: x[:2]=22
In [337]: x
Out[337]: array([22, 22,  2])
In [338]: y
Out[338]: array([0, 1, 2])

We get the same behavior if the object is a list:

In [339]: yl = [1,2,3]
In [340]: fun1(yl)
140296925836360 ...
140296824729096
Out[340]: [1, 2, 3, 1, 2, 3]

In [341]: fun2(yl)
140296925836360 ...
140296925836360
Out[341]: [23, 2, 3]
In [343]: yl
Out[343]: [23, 2, 3]
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for this explanation. It's useful to see it with the object ids and that approach should help me understand if I have any similar problems in future.
1

The only way to modify an object and keep the original intact, in general, is to copy the original, I think you're looking for something like:

def fun2(x):
    x = x.copy()
    for i in range(0, x.shape[0]):
        x[i, :] = 2*x[i, :]
    return x

Then answer to your question regarding the apparent difference between fun1 and fun2 is that python, like most OOP languages, is pass-by-object-reference. The first function reassigns the variable declared in the function signature, but doesn't mutate it's input. The second, simply mutates it's input. the To learn more about this, take a look at this article, https://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/

1 Comment

“Hamlet was not written by Shakespeare; it was merely written by a man named Shakespeare.” haha, thanks for the link
0

In fun1, executing x = 2*x reassigns the variable x to a new array. In fun2, the index assignments x[i, :] = __ directly modify the original array.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.