This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author esegall
Recipients docs@python, esegall
Date 2016-04-24.23:27:22
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1461540442.59.0.34324123093.issue26842@psf.upfronthosting.co.za>
In-reply-to
Content
I am using the tutorial to learn Python. I know many other languages, and I've taught programming language theory, but even so I found the warning in Section 4.7.1 about Default Argument Values to be confusing. After I spent some effort investigating what actually happens, I realized that the warning is incomplete. 

I'll suggest a fix below, after explaining what concerns me. 

Here is the warning in question:

-----------------------------------------------------------------
Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. ...

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

This will print

[1]
[1, 2]
[1, 2, 3]
-----------------------------------------------------------------

It's clear from this example that values are carried from one function invocation to another. That's pretty unusual behavior for a "traditional" function, but it's certainly not unheard of -- in C/C++/Java, you can preserve state across invocations by declaring that a local variable has static lifetime. When using this capability, though, it's essential to understand exactly what's happening -- or at least well enough to anticipate its behavior under a range of conditions. I don't believe the warning and example are sufficient to convey such an understanding. 

After playing with it for a while, I've concluded the following: "regular" local variables have the usual behavior (called "automatic" lifetime in C/C++ jargon), as do the function's formal parameters, EXCEPT when a default value is defined. Each default value is stored in a location that has static lifetime, and THAT is the reason it matters that (per the warning) the expression defining the default value is evaluated only once. 

This is very unfamiliar behavior -- I don't think I have used another modern language with this feature. So I think it's important that the explanation be very clear. 

I would like to suggest revising the warning and example to something more like the following: 

-----------------------------------------------------------------
Important warning: When you define a function with a default argument value, the expression defining the default value is evaluated only once, but the resultant value persists as long as the function is defined. If this value is a mutable object such as a list, dictionary, or instance of most classes, it is possible to change that object after the function is defined, and if you do that, the new (mutated) value will subsequently be used as the default value.  

For example, the following function accepts two arguments:

def f(a, L=[]):
    L.append(a)
    return L

This function is defined with a default value for its second formal parameter, called L. The expression that defines the default value denotes an empty list. When the function is defined, this expression is evaluated once. The resultant list is saved as the default value for L. 

Each time the function is called, it appends the first argument to the second one by invoking the second argument's append method. 

If we call the function with two arguments, the default value is not used. Instead, the list that is passed in as the second argument is modified. However, if we call the function with one argument, the default value is modified. 

Consider the following sequence of calls. First, we define a list and pass it in each time as the second argument. This list accumulates the first arguments, as follows: 


myList=[]
print(f(0, myList))
print(f(1, myList))

This will print: 

[0]
[0, 1]

As you can see, myList is being used to accumulate the values passed in to the first as the first argument.
 
If we then use the default value by passing in only one argument, as follows:

print(f(2))
print(f(3))

we will see: 

[2]
[2, 3]

Here, the two invocations appended values to to the default list. 

Let's continue, this time going back to myList:

print(f(4,myList))

Now the result will be:

[0, 1, 4]

because myList still contains the earlier values.

The default value still has its earlier values too, as we can see here:

print(f(5))

[2, 3, 5]

To summarize, there are two distinct cases: 

1) When the function is invoked with an argument list that includes a value for L, that L (the one being passed in) is changed by the function. 

2) When the function is invoked with an argument list that does not include a value for L, the default value for L is changed, and that change persists through future invocations. 
-----------------------------------------------------------------

I hope this is useful. I realize it is much longer than the original. I had hoped to make it shorter, but when I did I found I was glossing over important details.
History
Date User Action Args
2016-04-24 23:27:22esegallsetrecipients: + esegall, docs@python
2016-04-24 23:27:22esegallsetmessageid: <1461540442.59.0.34324123093.issue26842@psf.upfronthosting.co.za>
2016-04-24 23:27:22esegalllinkissue26842 messages
2016-04-24 23:27:22esegallcreate