Assigning to expressions (as opposed to names) is commonplace in Python. For example, this is perfectly valid syntax:
my.object["with_some"].very_long["expression"] = func(my.object["with_some"].very_long["expression"], my.object["with_some"].very_long["expression"])
however, if I try to shorten it using the walrus operator by making the LHS a named expression such as
(x:=my.object["with_some"].very_long["expression"]) = func(x, x)
Python raises a SyntaxError:
SyntaxError: cannot assign to named expression
Similarly, for x[0] in range(5)
is valid syntax (just mightily confusing), whereas for (a:=x[0]) in range(5)
is again a SyntaxError: cannot assign to named expression
.
Why can't I assign to a named expression? Is this by design or by implementation?
PEP 572 mentions some cases where the walrus operator cannot be used, but all but one are about the syntax of unparenthesised expressions and the final one is about f-strings. Unlike the situation pointed out in this answer ((self.x := ...)
), the assignment target within the walrus operator in my case is a simple name/identifier, not an expression. It's not clear from the language reference either why this is not allowed. Googling the error message today yields exactly three results at the time of writing: One issue about limitations in comprehensions, a Stack Overflow chat message expecting hundreds of Hot Network Questions (which didn't happen), and an issue in a 3rd-party Python parser; none help me.
What is the reason I cannot assign to a named expression? Is this a design rule that is documented or defined somewhere, or is it an implementation limitation? As far as I can see, it does not lead to any ambiguities and it would seem that my use case should be valid.
is syntactic sugar for
so it's not as symmetrical as you think. The original long expression's value, not the expression itself, is passed as the two arguments to
func
, and the original expression itself is only kind of the target of an assignment.You could, however, write
with
x
being assigned the value of the one common expression to the desugared version,my.object["with_some"].very_long
.The assignment expression has to be on the right-hand side of the assignment because that is evaluated before the left-hand side. Also, it has to be the first argument that uses
:=
, because function arguments are guaranteed to be evaluated from left to right.Here's the test I used to verify that the above should work, assuming I defined
A
appropriately.The new value of
a.b["foo"].b["foo"]
is"barbar"
, as expected from the definition offunc
.