Le Sat, 16 May 2009 12:21:13 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
On Sat, 16 May 2009 01:51:31 am spir wrote:
* requires people to learn one more feature (so newbies will still be confused that def f(x=[]) doesn't behave as they expect).
That's the relevant drawback for me. A solution that does not solve the issue. A new syntactic pattern to allow call time evaluation of defaults is a (costly) solution for people who don't need it.
Let me expand on 'costly'. You (nicely) totally rejected a previous proposal of mine, but it adressed the issues pointed here (propably not clearly enough, though). I stated that defaults are part of a func def, and should be cached when the definition is evaluated, in a way that they are separate from local vars. This means that a local var should not point to the same object as the one cached. I did not enter implementation stuff, but this obviously requires, I guess, that defaults are (deep?)copied into locals at call time, when the object is mutable. Pseudo code for def f(arg=whatever) # at definition time f.__defaults__["arg"] = whatever # at call time if <arg not provided by caller> if <cached object is safely immutable> arg = f.__defaults__["arg"] else: arg = copy(f.__defaults__["arg"]) The advantage is that if ever "whatever" is a complex expression, it will not be re-evaluated on each call. Unlike with the late-binding proposal. As I see it, re-evaluating 'whatever' at call time does not serve any purpose -- except possibly that the result may change at runtime intentionally, which is another topic. Actually, a default may be changed from inside (a mutable object updated through local var in the func's own body) or from outside (when the expression holds variable items). In the latter case, this may be intentional to get a kind of runtime-changing default value. See also below.
There is no solution to the problem of newbies' confusion. The standard behaviour will remain in Python 2.x and almost certainly Python 3.x. The earliest it could change is Python 3.3: it could be introduced with a "from __future__ import defaults" in 3.2 and become standard in 3.3.
(It almost certainly will never be the standard behaviour, but if it did, that would be the earliest it could happen.)
I agree with that, it well certainly never change; I would never have brought this topic back again myself. (It's such an obvious issue that I was sure it had been hundred times discussed since the late eighties ;-) Bit it seems to come back regularly anyway.
And even if it did change, then newbies will be surprised and upset that def f(x=y) doesn't behave as they expect. Here's the current behaviour:
y = result_of_some_complex_calculation() # => 11 def f(x=y): ... return x+1 ... f() 12 y = 45 f() 12
Given the proposed behaviour, that second call to f() would surprisingly return 46, or worse, raise a NameError if y is no longer in scope.
Yes, "surprisingly". That's the reason why I stated earlier that when a default in *intended* to change at runtime, it is worth making it clear with explicit code and even comments. My previous proposal was precisely to make the default fixed, so that this (silent) behaviour disappears. In the case of an intentional runtime-changing default, it is thus a Good Thing to use a sentinel -- and with my proposal we would have to do it because of defaults beeing cached at definition time. so to have the above behaviour, one would need to write: def f(x=SENTINEL): # y will change at runtime if x is SENTINEL: x = y return x+1 For an example: def writeIndent(indent_level, indent_token=SENTINEL): # 'indent_token' may be provided by the caller # to conform to the source beeing edited. # Else read it from current user config. if indent_token is SENTINEL: indent_token = config.indent_token .......
The real problem is that people don't have a consistent expectation for default arguments. No matter what behaviour Python uses, people will be caught out by it sometimes.
I rather think that an important fraction of experienced python programmers have expectations dictated by the current semantics they're used to. (Which is indeed correct. This a case of "intuitive = familiar".) But expectations from so to say "ordinary" people unaware of the said semantics are clearly different. The fact that people are bitten (when default is changed from inside the func), or merely surprised (your case above with default changed from outside) rather shows what they expect the func def to mean (both when writing and reading it). Denis ------ la vita e estrany