
On Wed, Nov 3, 2021 at 6:01 PM Stephen J. Turnbull <stephenjturnbull@gmail.com> wrote:
Steven D'Aprano writes:
"Write `arg=default` if you want the default to be evaluated just once, and `arg=late default` if you want the default to be evaluated each time it is needed. If you are not sure which one you need, use the first."
Which of course is ambiguous, since the argument may be referenced many times in the function body or only late in the body. Someone who doesn't yet understand how early binding of mutable defaults works is also somewhat likely to misunderstand "when needed" as a promise that resolves to an object the first time it is referenced, or as a thunk that gets run every time it is referenced, both of which are incorrect.
People will often have completely wrong understandings about things. While it's possible to avoid some of those, we can't hold the language back because *someone who doesn't understand* might misunderstand. Mutable objects in general tend to be misunderstood. Python has a strong policy of evaluating things immediately, or being very clear about when they will be evaluated (eg generators when you next() them). Resolving a promise to an object on first reference would break that pattern completely, so I would expect most people to assume that "each time it is needed" means "each time you omit the argument". And if they don't, they'll figure it out and go digging. Or not, as the case may be; I've seen misunderstandings linger in people's brains for a long time without ever being disproven. (My own brain included.)
What you should write to be (more) accurate is
Write `arg=default` if you want the default to be evaluted when the function is defined [and the value to be stored in the function to be used when it is called], and `arg=late default` if you want the default to be evaluated each time the function is called. If you are not sure which one you need, ask someone to help you, because using the wrong one is a common source of bugs.
The part in brackets is a gloss that might be helpful to the beginner, who might experience a WTF at "evaluated when defined"
And that's what happens when you need to be pedantically correct. Not particularly useful to a novice, especially with the FUD at the end.
To be honest, I was surprised you chose early binding for "when in doubt". I would expect that "early binding when late is appropriate" is a much more common bug for beginners than "late binding when early is appropriate". Of course I may be biased because it's the only bug now, but I would think that would continue to be true for beginners if late binding is made available. Especially when they're testing code in the interactive interpreter. There are probably many programs where the function is called with the argument missing only once, in which case it doesn't matter, until you invoke the function repeatedly in the same context.
There's a performance cost to late binding when the result will always be the same. The compiler could optimize "=>constant" to "=constant" at compilation time [1], but if it's not actually a compile-time constant, only a human can know that that can be done. But then the question becomes: should we recommend late-binding by default, with early-binding as an easy optimization? I'm disinclined to choose at this point, and will leave that up to educators and style guide authors. ChrisA [1] Technically, there'd still be a difference, but only if you mess with the function's dunders. So for safety, probably the compiler should never optimize it.