This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Allow multiple assignment (i.e. tuple on LHS) in walrus operator
Type: Stage:
Components: Interpreter Core Versions: Python 3.10
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: emilyemorehouse, gvanrossum, lys.nikolaou, pfalcon, rhettinger, tim.peters
Priority: normal Keywords:

Created on 2021-02-06 08:13 by pfalcon, last changed 2022-04-11 14:59 by admin.

Messages (3)
msg386551 - (view) Author: Paul Sokolovsky (pfalcon) * Date: 2021-02-06 08:13
Currently (CPython 3.10.0a4) having a tuple on left-hand side of assignment expression/operator (aka walrus operator) is not allowed:

>>> ((a, b) := (1, 2))
  File "<stdin>", line 1
    ((a, b) := (1, 2))
     ^
SyntaxError: cannot use assignment expressions with tuple

As far as can tell, there's no explicit consideration of this case in the PEP572. There closest it has is under the https://www.python.org/dev/peps/pep-0572/#differences-between-assignment-expressions-and-assignment-statements section, "Iterable packing and unpacking (both regular or extended forms) are not supported".

But note that the usecase presented above is better seen as "parallel assignment", not as "iterable unpacking".

Next issue: PEP572 puts heavy emphasis on the "named expression" usecase. I would like to emphasize, that "naming an expression" seems like *just one of usecases* for assignment operator. First and foremost assignment operator is, well, assignment.

With that in mind, ((a, b) := (1, 2)) is compatible with "naming expressions" idea, it "gives names" to multiple expressions at once.

There's a temptation to suggest that the above could be rewritten as:

((a := 1), (b := 2))

, and for the "naming" usecase, that would be true.  But as noted above, "naming" is just *one* of the usecases. Following can't be "sequentialized" like that:

((b, a) := (a, b))

And for as long as someone considers the following acceptable:

func(a := val)

There's little point to prohibit the following:

min((b, a) := (a, b))


And that's the main argument - consistency between assignment statement and assignment operator. For as long as this is allowed:

a, b = b, c

it makes sense to allow:

((a, b) := (b,c))

(Extra parens account for different in grammar and precedence for the operator).


This issue is posted to capture discussion initially posted to python-ideas/python-dev:

https://mail.python.org/archives/list/python-ideas@python.org/thread/6IFDG7PAFPHVPGULANOQDAHP2X743HCE/
https://mail.python.org/archives/list/python-ideas@python.org/thread/X3LNCCO6PHTLAQXHEAP6T3FFW5ZW5GR5/
https://mail.python.org/archives/list/python-dev@python.org/thread/6IFDG7PAFPHVPGULANOQDAHP2X743HCE/

, to serve as a reference to whoever may be searching the bugtracker for this case.
msg386564 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2021-02-06 17:40
+0 I think this is with worth considering.  Whenever the use case arises, the workaround is awkward.  Code would be clearer if assignment expressions were liberalized to allow unpacking.

The PEP aspired to avoid complex lvalues that might be hard to read or that had tricky semantics.  Unpacking is one the simpler cases and would be mostly harmless and sometimes beneficial (and not just for the uncommon case of wanting simultaneous assignment).
msg386620 - (view) Author: Paul Sokolovsky (pfalcon) * Date: 2021-02-08 07:31
Thanks.

I would like to put this ticket in the context of other grammar-related tickets/elaboration for the assignment operator:

https://bugs.python.org/issue42316 - allow foo[a:=1] instead of foo[(a:=1)]
https://bugs.python.org/issue42374 - allow (c := i for i in b) instead of ((c := i) for i in b)
https://bugs.python.org/issue42381 - allow {i := 0 for i in b} instead of {(i := 0) for i in b}

All of the above were implemented. Of course, allow parallel assignment, which was previously disabled, is somewhat a bigger change then allowing unparenthesized assignment usage. But from the grammar point of view, they are of the same nature (using walrus-aware term at right place). The initial patch I have on my hands is:
 
 named_expression[expr_ty]:
-    | a=NAME ':=' ~ b=expression { _Py_NamedExpr(CHECK(expr_ty, _PyPegen_set_expr_context(p, a, Store)), b, EXTRA) }
+    | a=star_targets ':=' ~ b=expression { _Py_NamedExpr(CHECK(expr_ty, _PyPegen_set_expr_context(p, a, Store)), b, EXTRA) }


And let's review the 'foo[(a := 1)]' case from more angles. C had assignment as expression from the very beginning, but in all fairness, I never saw "foo[a = 1]" in my whole life (well, maybe in https://www.ioccc.org/ submissions). But we see that with Python, people are not too shy to use that. And they even submit issues like "hey, I don't want to write foo[(a := 1)], I want to write foo[a := 1] !" And they don't get replies like "you know, doing assignment in index would hamper readability/maintainability; and those extra parens are there exactly to hint you more about this fact". Instead, the case is getting acked and fixed.

So, under such circumstances, I don't think that writing "min((a, b) := (b, a))" should be considered "much worse" than "foo[a := 1]", and should be kept disallowed (as again, fix for both is of the same nature).
History
Date User Action Args
2022-04-11 14:59:41adminsetgithub: 87309
2021-02-08 07:31:26pfalconsetmessages: + msg386620
2021-02-08 07:23:11pfalconsetnosy: + lys.nikolaou
2021-02-06 17:40:51rhettingersetnosy: + gvanrossum, tim.peters, rhettinger, emilyemorehouse

messages: + msg386564
versions: + Python 3.10
2021-02-06 08:13:07pfalconcreate