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. > 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. -- Steven -- http://mail.python.org/mailman/listinfo/python-list