
On Tue, Oct 26, 2021 at 01:32:58AM +0100, Rob Cliffe via Python-ideas wrote:
Syntax bikeshedding: I still favour var := expr That clashes with the walrus operator. Remember that the walrus operator can appear inside the expression:
var:=spam+eggs:=(something+other) or eggs That is a SyntaxError. I'm not sure what you mean, my best effort to make it legal is (var:=spam+(eggs:=(something+other) or eggs)) And I don't understand what point you're making here. Yes, the walrus operator can appear in various places, how is that relevant? You could write def f(a := (b := c)): which might be a tad confusing but would be unambiguous and legal, just as def f(a = (b := c)): is currently legal (I tested it). I don't see a clash.
Modifying the assignment symbol is wrong. This is not a new kind of assignment, it should use the same `=` regular assignment. We are tagging the parameter to use late-binding, not using a different sort of assignment. The tag should be on the parameter name, not the assignment. With respect, it IS a new kind of assignment. One which happens at a different time (and whose value may vary in multiple calls of the function). The value (however calculated) is assigned to the
On 26/10/2021 02:56, Steven D'Aprano wrote: parameter. Once assigned, the parameter and its value are indistinguishable from ones that used early binding, or indeed had a value passed by the caller. It is not a new kind of parameter (in that sense).
IMO the similarity to early binding syntax is a good thing (or at least not a bad thing). Right, because binding is binding, and we should use the same `=`.
See above.
Just as the walrus operator is similar to `=` - after all they are both a form of assignment. But the walrus is a different form of assignment, it is an expression, not a statement.
Function parameter defaults are not literally statements, they are declarations, which are a kind of statement.
I don't think argument defaults should be allowed to refer to later arguments (or of course the current argument). That's making the interpreter's task too complicated, not to mention (surely?) inefficient. And it's confusing. Worst case, the interpreter has to do two passes over the parameters instead of one. The inefficiency is negligible. Perhaps I wasn't clear. When I said 'inefficiency', I meant to refer to cases like def f(a := b+1, b := e+1, c := a+1, d := 42, e := d+1) where late-binding defaults are allowed to refer to subsequent arguments. Here Python has to work out to assign first to d, then e,
As for confusing, I think you are conflating "it's new" for "it is confusing". You aren't confused. I doubt that anyone capable of writing a Python function would be confused by the concept:
def func(a=1, @b=c+1, c=2):
is no more confusing than the status quo:
def func(a=1, b=None, c=2): if b is None: b = c + 1 Confusing is perhaps the wrong word. I think the first example IS harder to read. When you read the first, you have to read a, then b,
True of course. But it is also an assignment, in that the value on the RHS of the walrus is assigned to the variable on the LHS, just as with a regular assignment. then b, then a, and finally c, which AFAICS requires multiple passes. But if it can all be worked out at compile time, it's not a runtime efficiency problem, though to my mind really obfuscated (why not write the arguments in the order they are intended to be calculated?). But to play Devil's Advocate for a moment, here is a possible use case: def DrawCircle(centre, radius := circumference / TWO_PI, circumference := radius * TWO_PI): # Either radius or circumference can be passed, whichever is more convenient then 'oh what is the default value of b, I'll look at c', then skip back to b to see what the intention is, then forward again to c because you're interested in that too. It would be better written as def func(a=1, c=2, @b=c+1): There is some to-and-froing in the second example too, but the function header has fewer symbols and is easier to take in. The information is presented in more, smaller chunks (3 lines instead of 1). (Of course, this kind of argument could be used against all argument defaults (including early-bound ones), and a lot of other convenient language features as well. We have to use our common sense/intuition/judgement to decide when conciseness outweighs explicitness.)
If you can understand the second, you can understand the first. All you have to remember is that:
1. positional arguments are bound to parameters first, left to right;
2. keyword arguments are bound to parameters next;
3. regular (early bound) defaults are bound next;
4. and lastly, late-bound defaults are bound.
Easey-peasey.
I really wish people would stop assuming that fellow Python coders are knuckle-dragging troglodytes incapable of learning behaviour equivalent to behaviour they have already learned:
I don't. But equally I don't want to make their lives harder than they need be. If it turns out that binding all early-bound defaults before binding all late-bound defaults is the best solution (and there is support for this position), fine. It makes some cases legal and working that wouldn't be otherwise. My initial reaction (preferring strict L-to-R evaluation of all defaults) was very likely wrong.
The status quo:
1. positional arguments are bound to parameters first, left to right;
2. keyword arguments are bound to parameters next;
3. regular (early bound) defaults are bound last.
All we're doing is adding one more step. If that is confusing to people, wait until you discover classes and operator precedence!
x = 2*3**4 - 1
FWIW I have no trouble parsing that (although I came up with an incorrect answer of 80 because I forgot to actually do the `2*` 🙁).
[snip]
(Maybe even as late as *on need* rather than before the body of the function is entered. That would be really nice, but maybe too hard to implement.)
Never mind hard-to-implement, it would be REALLY confusing. When the default value is calculated up front, you know what it is (so to speak). If it could be calculated deep inside the function, perhaps in multiple places giving different answers, perhaps used repeatedly in a loop (but only calculated the first time), perhaps not calculated at all in some code paths, that's obfuscation and abuse. Better to use a sentinel value. Or something. Rob Cliffe