0

Hope I doesn't repeat any question, but couldn't find...

I'm trying to run a function with the same key parameter many times. I understand why the f function changes the x0 array, but I don't really understand why the g function takes every time different argument (y0 is constant). I would be thankful if anyone can explain me this behaviour and give me a tip how to implement what I want (basically at the end I would like to have y == np.array([0, 30, 0]) ).

import numpy as np

x0 = np.zeros(3)
y0 = np.zeros(3)

def f(i, x = x0):
    x[1] += i
    return x

def g(i, y = y0.copy()):
    print "y that goes to g (every time is different) \n", y
    y[1] += i
    return y

print "x0 before f \n" ,x0
x = f(5)
print "x0 after f is the same as x  \n", x0, "\n", x

print "y0 before g \n" ,y0
for i in [10, 20, 30]:
    y = g(i)
print "y0 after g doe not change, but y is NOT as I would expect! \n", y0, "\n", y
d_j
  • 1,119
  • 1
  • 9
  • 16
  • `y0.copy()` is also evaluated when the function is created and returns a new array. That same array will be used for every function call. – mgilson May 13 '14 at 20:26
  • This is not a duplicate. The question has two parts, `x = x0` and `y = y0.copy()`. The first part is answered by the earlier question (which the OP already understood) but the second part is new. – Hans Then May 13 '14 at 20:26
  • @HansThen -- But `y0.copy()` just returns a mutable object which is used as the default argument. If there is a difference, it's _really_ minimal (IMHO). Although, I have to admit, I was shocked when StackOverflow marked this as a dupe after _only my vote_. Usually it takes 5... – mgilson May 13 '14 at 20:27
  • 1
    It is the difference between having a mutable object as default argument and evaluating a function only once. Suppose the function had `myarg=random.randint()` as default argument. What's more, the OP explicitly indicated that he understood the effect of a mutable default argument, but not why the function call had the same effect. – Hans Then May 13 '14 at 20:31

2 Answers2

4

Default arguments to functions are evaluated only once, when the function is defined. This means that your function definition is equivalent to:

y0_ = y0.copy()
def g(i, y = y0_):
    print "y that goes to g (every time is different) \n", y
    etc

Which explains why your y argument changes every time.

Hans Then
  • 10,935
  • 3
  • 32
  • 51
  • Ok, I didn't expect that default arguments are evaluated only once... This explains a lot, basically there is no really difference between functions `f` and `g` in terms of results (only doesn't change the initial y0). I would have to copy the `y0` array within a loop in this simple example. – d_j May 13 '14 at 20:44
  • Another way would be to use `None` as default argument and then use `y = y if y else y0.copy()` or (less pythonic) `y = y or y0.copy()`. – Hans Then May 13 '14 at 20:47
  • I think the best way to fix this is: `def g(i, y=y0): y = y.copy()`. It's simple and works whether the default is taken or not. – Mark Ransom May 13 '14 at 22:23
  • It depends a bit if the original `y` was supposed to be mutable. – Hans Then May 14 '14 at 05:22
  • If it's intended to be mutated then it doesn't make sense to have a default parameter. – Mark Ransom May 14 '14 at 14:28
0

"but I don't really understand why the g function takes every time different argument"

def g(i, y = y0.copy()):
    ....

Your y0 is constant but you are creating copy of y0 with different reference first time when g() function is called so you can't change y0 with function g(). Just change

y = y0.copy() 

to

y=y0
Josip Grggurica
  • 421
  • 4
  • 12