6

I understand that one should not use mutable default parameter value in Python (with some exceptions) because this value is evaluated and stored only once when the function is defined, and not each time the function is later called.

My understanding of that is this (using the example below; please excuse my imprecise language as I'm just a beginner in Python programming, who's stuck at the Function chapter of my textbook because of this):

def f(x = [1, 2, 3]):
    x.append(4)
    print(x)

f()
f()

1) The function f is defined, and x (local variable in f) assumes the default variable of [1, 2, 3] (even before the function is called)

2) When f() is called, x is still [1, 2, 3] due to no argument passed to it, and x continues having its default value

3) x is modified in place with append, becomes [1, 2, 3, 4], and is printed as such

However, this is where my confusion arises. I'd assume that:

4) When f ends, x is destroyed (in the stack or whatever you'd call it) and is no longer associated with the list object [1, 2, 3, 4]**

5) The list object [1, 2, 3, 4] is reclaimed since there's no variable that refers to it anymore

Therefore,

6) When f() is called the second time, I'd expect Python to output an error since x now no longer has a value associated with it. In other words, how can Python reuse the default value from the last evaluation when it's been reclaimed/destroyed?

Appreciate all your help and explanation!

** this understanding I got from Ned Batchelder's page on variable name assignment (see below) Net Batchelder's variable assignment function

seismatica
  • 437
  • 1
  • 6
  • 19

2 Answers2

9

While it may seems to you that at the end of the execution x, the default value, is disposed, it is not.

In fact, Python has a global namespace with all the names available for you to use (built-in functions, classes and functions you import or define).

The content of this namespace is made of objects. Function are objects too.

As a test, if you try this in a script or in the python command line, you will see what I mean:

def f(x = [1, 2, 3]):
    x.append(4)
    print(x)
print dir(f)

you will see the object nature of the function f. As an objects, the default values are referenced in an attribute, f.func_defaults, therefore they are always available and if mutable they retain the changes, giving you side effects with you may not want.

EDIT: in python 3 the attribute has been replaced by f.__defaults__

bracco23
  • 2,181
  • 10
  • 28
  • Thank you for your explanation bracco23! I tried print(dir(f)), and only saw `__defaults__` as the relevant attribute. Is this the same as your func_defaults? Perhaps this is due to my running Python 3 and yours Python 2. – seismatica Sep 25 '17 at 10:04
  • Ah okay `func_defaults` was indeed replaced by `__defaults__` in Python 3 (https://stackoverflow.com/questions/8806530/accessing-the-default-argument-values-in-python) – seismatica Sep 25 '17 at 10:07
  • yes, as you may have already guessed by printing it, in python 3 `__defaults__` is the attribute which references the default values of the parameters. – bracco23 Sep 25 '17 at 10:07
  • I was also surprised at first. but after thinking for a while on that I've got there is no other way to handle that. defaults should be calculated _only once_ since they could be an expression rather simple value. this way it could be really weird if some function was called again and again just to calculate default value. – skyboyer Jan 02 '18 at 22:01
3

There are two references to the list in your case, one is store in the background of the function as the default value to the argument x.

When the function is called without x, a new reference to the same list is created as the local variable x. Then you append to the list via the second reference. And after the call, the second reference is garbage collected. The first reference still points to the same list, which has one element more now.

Or in short: there is only one list all the time.

Klaus D.
  • 13,874
  • 5
  • 41
  • 48
  • Thank you Klaus D.! This really helps me understand this quirk. BTW is the default value reference you mentioned the same thing as bracco23's func_defaults attribute? If so then your 2 answers explain my question perfectly. – seismatica Sep 25 '17 at 10:06
  • Yes, it is. I did not write about since the implementation change with the Python version and I tried to keep the explanation as simple as possible. – Klaus D. Sep 25 '17 at 10:37