Re: Early and late binding [was Re: what does 'a=b=c=[]' do]

On Fri, 23 Dec 2011 17:03:11 +0000, Neil Cerutti wrote:

The disadvantage of late binding is that since the expression is live,
it needs to be calculated each time, even if it turns out to be the
same result. But there's no guarantee that it will return the same
result each time:

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.

consider a default value like x=time.time(), which will return a
different value each time it is called; or one like x=a+b, which will
vary if either a or b are changed. Or will fail altogether if either a
or b are deleted. This will surprise some people some of the time and
lead to demands that Python "fix" the "obviously buggy" default
argument gotcha.

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.

much harder to figure out what's going wrong with an early-bound

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

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

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.