That's its main *advantage*.
Ah yes, sorry, poor wording on my part. Whether calculating the default
value *once* or *each time* is an advantage or disadvantage depends on
what you're trying to do. Either way, it could be just what you want, or
an annoying source of bugs.
It's hard to see anyone being confused by the resultant exception.
That's because you're coming at it from the perspective of somebody who
knows what to expect, in the middle of a discussion about the semantics
of late binding. Now imagine you're a newbie who has never thought about
the details of when the default value is created, but has a function like
"def foo(x, y=a+b)". He calls foo(x) seven times and it works, and on the
eighth time it blows up, perhaps with a NameError. It's surprising
behaviour, and newbies aren't good at diagnosing surprising bugs.
Or worse, it doesn't blow up at all, but gives some invalid value that
causes your calculations to be completely wrong. Exceptions are not the
worst bug to have -- they are the best.
It's
much harder to figure out what's going wrong with an early-bound
mutable.
Only for those who don't understand, or aren't thinking about, Python's
object model. The behaviour of early-bound mutables is obvious and clear
once you think about it, but it does require you to think about what's
going on under the hood, so to speak.
[...]
To fake early binding when the language provides late binding, you
still use a sentinel value, but the initialization code creating the
default value is outside the body of the function, usually in a global
variable:
[...]
I'd use a function attribute.
def func(x, y=None):
if y is None:
y = func.default_y
...
func.default_y = []
That's awkward only if you believe function attributes are awkward.
I do. All you've done is move the default from *before* the function is
defined to *after* the function is defined, instead of keeping it in the
function definition. It's still separate, and if the function is renamed
your code stops working. In other words, it violates encapsulation of the
function.
That's not to say that you shouldn't do this. It's a perfectly reasonable
technique, and I've used it myself, but it's not as elegant as the
current Python default argument behaviour.
[...]
The greater efficiency was probably what decided this question for
Python, right? Since late-binding is so easy to fake, is hardly ever
what you want, and would make all code slower, why do it?
Excellent point.