Walrus fails with []

these are invalid,

([x] := [1])

gives,

SyntaxError: cannot use assignment expressions with list

similarly,

([x, *_] := [1, 2])

also fails with the same error.

What could be the reasoning that they decide to not allow such a syntax?
I do not see any problem with them.

1 Like

To me, it is disabled because it may be a bit confusing(and a bit controversial). Will this expression be equal to [1], or x ?
Just my idea.

what could be controversial here,
let us say we have,

print([[x := [i*2], x] for i in range(1, 5)])

which outputs,

[[[2], [2]], [[4], [4]], [[6], [6]], [[8], [8]]]

but if we instead use,

print([[[x] := [i*2], x] for i in range(1, 5)])

then it should output,

[[[2], 2], [[4], 4], [[6], 6], [[8], 8]]

but the second one is not valid for some unknown reason.

I will try to explain my perspective but, again, it is just my thought.
You have : [x] := [i*2] and I found 2 different scenarios (with two different results) about what the output may be:

Scenario 1

It will return x, which is equal to i*2.

Scenario 2

It will return what you have typed before the operator(since you haven’t written just ‘x’, but [x]):
It will return [x] which is equal to [i*2]

Scenario 3

It will return what you have typed after operator(value) : `[i*2]`

Note: Found last scenario ridiculous and decided to remove it.

I was investigating this a bit more,
it turns out that I cannot use walrus with tuple also,
so,

print([[(x, y) := (i*2, i*3), x, y] for i in range(1, 5)])

gives,

SyntaxError: cannot use assignment expressions with tuple

but the way I want it to work is, it outputs,

[[(2, 3), 2, 3], [(4, 6), 4, 6], [(6, 9), 6, 9], [(8, 12), 8, 12]]

I assume, when I write,

[[[x] := [i*2], x] for i in range(1, 5)]

then the, [x] := [i*2] part should do two things,

  1. return [i*2] (that is, whatever comes after the walrus operator)
  2. set [x] = [i*2]

and this value of x would be used in the second entry of the list in my previous post, that is here,

[[x] := [i*2], x]
               ^

Remember: the := operator does not do anything that isn’t possible without it, so what would be the code without using the assignment expression operator?

It is a deliberate design. See the PEP:

“Iterable packing and unpacking (both regular or extended forms) are not supported”

The reason isn’t given. If you really care, you could dig through the discussions on Python-Ideas and Python-Dev mailing lists, but be warned, there are hundreds of posts.

I expect that the reason is one or both of:

  • too hard to get the parser to understand it;
  • too confusing to read for the human reader.

The walrus operator was hugely controversial, it was so stressful with so much opposition and negativity that it drove Guido into retiring as the BDFL of the Python language. So it may be that this was just a compromise position, prohibiting a minor corner case that increases complexity without actually being very useful.

without using the walrus, one would have to recompute,

[[(i*2, i*3), i*2, i*3] for i in range(1, 5)]
[[[i*2], i*2] for i in range(1, 5)]

(had to recompute i*2, i*3)

the other way to do this could be,

[[(x := i*2, y := i*3), x, y] for i in range(1, 5)] # but had to repeat walrus
[[[x := i*2], x] for i in range(1, 5)]

maybe they do not want to make it more complicated, as for someone reading walrus for the first time, it would be a bit confusing on its own.
but if overtime walrus becomes more popular, then I think they will have to reconsider iterable packing and unpacking.

When I code, I always keep in mind: The Zen of Python
In particular, these points:

  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Readability counts.

I can see that ‘walrus’ can address some of those points.

The biggest issues will be backwards compatibly. There are many systems that are still using Python2.x, and a huge number that have yet to move away from Python 3.6, so I think it’ll be years down the road before we see ‘walrus’ being used in general.

There is no need to recompute:

stream = ((i*2, i*3) for i in range(1, 5))
data = [[[x,y], x,y] for x, y in stream]

[[[2, 3], 2, 3], [[4, 6], 4, 6], [[6, 9], 6, 9], [[8, 12], 8, 12]]

Based on that one can construct hard to read (but without recompute) one-liner as well:

data = [[[x, y], x, y] for x, y in ((i*2, i*3) for i in range(1, 5))]

Scenario 2

It will return what you have typed before the operator (since you haven’t written just ‘x’, but [x]):
It will return [x] which is equal to [i*2]

That scenario is the simple and consistent way to do it. It’s also how chained assignment works, so shouldn’t it also be how the assignment expression works?