Sometimes you can explain a simple thing for the thousandth time, and come away with a deeper understanding yourself. It happened to me the other day with Python mutable argument default values.
This is a classic Python “gotcha”: you can provide a default value for a function argument, but it will only be evaluated once:
>>> def doubled(item, the_list=[]):
... the_list.append(item)
... the_list.append(item)
... return the_list
...
>>> print(doubled(10))
[10, 10]
>>> print(doubled(99))
[10, 10, 99, 99] # WHAT!?
I’ve seen people be surprised by this and ask about it countless times. And countless times I’ve said, “Yup, the value is only calculated once, and stored on the function.”
But recently I heard someone answer with, “it’s a value, not an expression,” which is a good succinct way to say it. And when a co-worker brought it up again the other day, I realized, it’s right in the name: people ask about “default values” not “default expressions.” Of course it’s calculated only once, it’s a default value, not a default expression. Somehow answering the question for the thousandth time made those words click into place and make a connection I hadn’t realized before.
Maybe this seems obvious to others who have been fielding this question, but to me it was a satisfying alignment of the terminology and the semantics. I’d been using the words for years, but hadn’t seen them as so right before.
This is one of the reasons I’m always interested to help new learners: even well-trodden paths can reveal new insights.
Comments
This was a bit confusing to read, the first argument is ‘val’ but it appears you are referring to “the value” as “[]”, meaning the default value of “the_list”. Changing the name of the first arg val might be less confusing for the reader, if I understand this article correctly (which I might not lol)
@Dan, I see what you mean. I changed it from
val
toitem
. And you are understanding it correctly!I have no idea how I’ve never hit this in my career, but now I’m paranoid about how many ticking time bombs are in my code…
Hi, I should say that it’s actually “expression, not value” 😅
Because strictly speaking, default argument gets a value of an expression that is evaluated when the function object is created. Sure, it’s evaluated only once (upon creation and not upon invocations) but it’s still an expression. Note that on the right side of
is an expression, too.
Here’s an example of default arg which clearly uses an expression:
In general it goes like this in language grammar:
So technically it’s “expressions, not values” :)
But sure, I understand your point!
This is tortured. The default argument gets a value which is computed (once!) by an expression. You could also say the default value is a series of characters, which are parsed into an expression, which is evaluated to produce a value. That’s all true, but it doesn’t add anything to the understanding.
The important part is that a function stores a value for the default, it does not store an expression. When people are surprised at Python’s behavior, it’s because they thought the default value was a stored expression which would be evaluated at every call. It’s not. It’s a value.
Aha, I see what you mean; indeed, I can see how one could think it’s a “stored expression” which should be evaluated upon each invocation. This makes sense!
How one will fix the example?
To Newbie
Maybe like this, at each call the_list it’s initialized:
Add a comment: