
[Guido]
(I vaguely recall this has been brought up before, but I'm too lazy to find the subtread. So it goes.)
PEP 572 currently seems to specify that when used in expressions, the precedence of `:=` is lower (i.e. it binds more tightly)
Umm ... that's the opposite of what the Reference Manual says "lower":means: """ 6.16. Operator precedence The following table summarizes the operator precedence in Python, from lowest precedence (least binding) to highest precedence (most binding). """
than all operators except for the comma.
Which gets more potentially confusing become the comma isn't listed at all as "an operator". Instead the meaning of commas for building tuples is captured by the higher-level `expression-list` grammar production: expression_list ::= expression ( "," expression )* [","] So _every_ mere operator "binds more tightly" (if you view it in those terms) than a comma in an expression list. What was mostly discussed before - after giving up on fully generally "assignment expressions" - was whether ":=" should get a precedence between comparison and bitwise OR operators. So that, e.g., parens wouldn't be needed in if x := match() is not None: to get the intended if (x := match()) is not None: But that never gained much traction, and it was dropped quickly. It was left with a (possibly unwritten!) consensus that ":=" should bind very weakly as an operator. Of the binary infix operators, the most weakly binding (the weakest of which now is Boolean OR).
I derive this from the single example `stuff = [[y := f(x), x/y] for x in range(5)]`.`
As above, the part you're looking at there falls under the expression_list part of the grammar, and no binary operator could group as y OP (f(x), x/y) instead. All binary operators group as (y OP f(x)), (x/y)
From this it would follow that `f(a := 1, a)`
And now you're throwing in the entirely different meaning of commas in argument lists ;-) But same thing: no operator can cross comma boundaries in argument lists, because the grammar of argument lists doesn't allow for that either. Even; e.g., f(lambda x: x, 2) groups as f((lambda x: x), 2)
is equivalent to `a = 1; f(1, 1)`,
Yup.
and also that `(a := 1, a)` is equivalent to `a = 1; (1, 1)`. (Although M.A.L. objected to this.)
That's just another instance of expression_list. Nothing so far has surprised me, because I have been viewing ":=" as an operator for some time now.
But what should `a := 1, 1` at the top level (as a statement) do? On the one hand, analogy with the above suggest that it is equivalent to `a = 1; (1, 1)`.
If it's an operator, there's really no choice about that.
But on the other hand, it would be really strange if the following two lines had different meanings:
a = 1, 1 # a = (1, 1) a := 1, 1 # a = 1; (1, 1)
Agreed.
I now think that the best way out is to rule `:=` in the top level expression of an expression statement completely (it would still be okay inside parentheses, where it would bind tighter than comma).
Probably prudent, and no objections here.
An alternative would be to make `:=` bind less tight than comma (like `=`) everywhere, so that `a := 1, 1` indeed meant the same as `a = 1, 1`. But that feels very wrong at least for the case `f(a := 1, 1)` -- I believe Tim already mentioned that we've been conditioned by keyword arguments to parse this as `f((a := 1), 1)`. (I could add to this that we have done various things to generator expression syntax to avoid ever having to deal with ambiguities like `a, a+1 for a in range(10)` or `a for a in x, y`.)
Offhand, since comma is not formally "an operator" now, I expect it would require major surgery to the grammar to have a := 1, 1 group as a := (1, 1) in any context. At least if ";=" is treated as "just another operator" and doesn't grow its own unique-to-it pile of grammar rules.
Another alternative would be to always require parentheses around `:=`, so that we would have to write `f((a := 1), 1)`. That's unambiguous, but otherwise just gets on the nerves.
I hoped that rigidly calling these "binding expressions" would break the connection in peoples' minds that these somehow "should behave" like assignment statements, but that seems futile. There's really nothing surprising here _if_ it's viewed as just another operator. Nobody would be tempted to, e.g., add parens to f(a + 1, 1), or if `+` were any other binary operator either. So, over time, it would be just as annoying need to type them for the `:=` operator.