0

After reading this thread, I also tried to get my hands dirty with default arguments. So, following is the same function having the mutable default argument:-

def foo(x = []):
    x.append(1)
    return x

As defined in the docs, the default value is evaluated only once when the function is defined.
So, upon executing this statement, print(foo(), foo(), foo()), I expected the output to be like this: [1] [1, 1] [1, 1, 1]

Instead, this is what I actually got as an output:-

>>> print(foo(), foo(), foo())
[1, 1, 1] [1, 1, 1] [1, 1, 1]

The way the statement is executed (according to me) is that the 1st function call returns [1], the 2nd returns [1, 1] and the 3rd function call returns [1, 1, 1] but it's the 3rd function call return value only which is printed repeatedly.

Also, printing the same function return values as separate statements(as mentioned in that thread) gives expected result, i.e.,

>>> print(foo())
[1]
>>> print(foo())
[1, 1]
>>> print(foo())
[1, 1, 1]

So, why printing the same function return values together doesn't return the output the way it does when executed separately?

Perspicacious
  • 594
  • 3
  • 17
  • 2
    Your `print(foo(), foo(), foo())` evaluates each `foo()` in turn, but the call to `print()` only occurs once all calls to `foo()` have finished. Also each return from `foo()` returns a reference to the **same** list. So `print()` just prints the same thing three times. – quamrana Jun 30 '20 at 16:08
  • 2
    Functions actually return references to objects. In first variant, the print happens after all foo calls and print receives three references to the same list in the state after third "foo". – Michael Butscher Jun 30 '20 at 16:08
  • 2
    I hate to refer you back to the link that inspired your experiment, but the reason it behaves this way in your test is the same as the reason why it behaves as it does for separate calls, and is explained in the effbot link given there: because the default value bound to the parameter is the **same actual object** each time. You see different results from different statements because **that object** has different contents each time; you see the same result multiple times when called in a single statement because you are looking at **that same object** multiple times **after** all the work. – Karl Knechtel Jun 30 '20 at 16:09
  • 1
    Mandatory link to [Ned Batchelder](https://nedbatchelder.com/text/names.html) – quamrana Jun 30 '20 at 16:11
  • If you do `print(foo())` one at a time, it does output what you expect. I indeed suppose that in a combined print statement, all calls are executed first after which the list has value [1,1,1] which is then printed. – Ronald Jun 30 '20 at 16:12

2 Answers2

1

What you didn't try in your second example is to save the return references like this:

a = foo()
print(a)
b = foo()
print(b)
c = foo()
print(c)

print(a, b, c)

Output:

[1]
[1, 1]
[1, 1, 1]
[1, 1, 1] [1, 1, 1] [1, 1, 1]

Now you should be able to see that a, b, c actually refer to the same list which has different contents at different times.

quamrana
  • 37,849
  • 12
  • 53
  • 71
0

the argument L is evaluated only once in the arg line, but by the time print function is executed the list is mutated to [1, 1, 1]. The most simple solution to prevent this is to copy/duplicate the list content each time.

print(foo()[:], foo()[:], foo())
Serge
  • 3,387
  • 3
  • 16
  • 34