New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can assign [] = (), but not () = [] #67464
Comments
>>> [] = ()
>>> () = []
File "<stdin>", line 1
SyntaxError: can't assign to () This contradicts the assignment grammar, which would make both illegal: https://docs.python.org/3/reference/simple_stmts.html#assignment-statements |
My guess is that it is not worth complicating the parser in order to make these two cases consistent, and it should be treated as a doc error. We'll see what other developers think. |
The starting point is recognizing that this has been around for very long time and is harmless. |
It seems that assigning to [] is the odd one out in this case. Why is this even possible? >>> [] = ()
>>> [] = {}
>>> [] = set()
>>> list() = ()
File "<stdin>", line 1
SyntaxError: can't assign to function call
>>> () = []
File "<stdin>", line 1
SyntaxError: can't assign to ()
>>> {} = []
File "<stdin>", line 1
SyntaxError: can't assign to literal
>>> set() = []
File "<stdin>", line 1
SyntaxError: can't assign to function call
>>> |
But () is the odd one out if you consider >>> [a, b] = range(2)
>>> [] = range(0)
>>> (a, b) = range(2)
>>> () = range(0)
File "<stdin>", line 1
SyntaxError: can't assign to () |
In ast.c, set_context checks for assignment to an empty tuple, but not an empty list. case List_kind:
e->v.List.ctx = ctx;
s = e->v.List.elts;
break;
case Tuple_kind:
if (asdl_seq_LEN(e->v.Tuple.elts)) {
e->v.Tuple.ctx = ctx;
s = e->v.Tuple.elts;
}
else {
expr_name = "()";
}
break; https://hg.python.org/cpython/file/ab2c023a9432/Python/ast.c#l912 |
As Raymond notes, this is a fairly harmless quirk - it changes a SyntaxError to an iterable length dependent ValueError: >>> () = []
File "<stdin>", line 1
SyntaxError: can't assign to ()
>>> [] = ()
>>> [] = [1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 0) I only found out about this after being puzzled when a typo in a live demo at PyCon 2015 failed to fail as I expected after seeing the presenter type a "[]" into the LHS of an assignment instead of the intended "_". Removing the data dependence to make the assignment fail immediately (even if never tested against a non-empty iterable) would involve making the handling of List_kind match that of Tuple_kind in the switch statement that eryksun quoted. I don't think it's an urgent fix, but if someone wanted to fix it (including a new test), I think it would be a reasonable contribution to accept. |
I would prefer this be fixed in the opposite direction, to allow “unpacking” an empty iterable using round brackets. I have used this syntax on purpose as a concise way to ensure that a generator is exhaused with no more yields: >>> def gen():
... yield "partial computation"
... print("computation allowed to complete")
...
>>> g = gen()
>>> next(g)
'partial computation'
>>> [] = g
computation allowed to complete |
There is also no reason to break currently working code, which turning this into a syntax error would od. |
Agreed. To take one example, David Beazley's PyCon 2015 talk would have been broken by the suggested change! (See https://www.youtube.com/watch?v=MCs5OvhV9S4, at around the 42:17 mark.) If there's any code change resulting from this issue, I also think it should be to make assignment to |
I don't have a strong opinion on this, but here is a patch to make () = [] a valid assignment. |
About the patch: I'm sure there are other tests to change,
>>> () = 1
Traceback (most recent call last):
File "<doctest test.test_syntax[3]>", line 1
SyntaxError: can't assign to () |
isn't it logical? [] is a mutable data structure (b, a) = [1, 2] is fine because a and b are mutable |
[a, b] = (1, 2) is also fine. |
I prefer to unpack into square brackets in general because it is a mnemonic for the star argument being a list: >>> (a, *b) = range(3)
>>> a
0
>>> b # A list, even though it was unpacked using tuple-like syntax
[1, 2] |
Berker's patch looks good.
The patch needs to updated:
|
but you don’t assign to data structures, but to names. you *modify* data structures. and in the square bracket assignment syntax you don’t modify the list created by the []. in fact the [] never even create a list. ————————————————————————————— also it’s news to me that [a, b] = range(2) works! i always did a, b = range(2), and knew that (a, b) = range(2) works. but assigning to something looking like a list literal is new and surprising to me. (and i thought i’ve mastered every corner of python’s syntax) |
+1 for Martin's suggestion of removing the inconsistency the other way (i.e. allowing "() = iterable" to mean the same thing as "[] = iterable", effectively asserting that an iterable is empty) I also agree with Raymond that it doesn't need to be mentioned in the What's New doc, just in the NEWS file. |
Thanks for the reviews. Here is an updated patch. |
I welcome this patch to go ahead. But the documentation <https://docs.python.org/3.6/reference/simple_stmts.html#assignment-statements\> also needs updating (see original post). I think it should mention that “target_list” can be empty. And it should use “iterable” instead of “sequence” in more places. |
Martin, do you want to take it from here? |
I missed Martin's comment about the documentation. I will update my patch. |
Okay I’ll let Berker update his patch, but I’m happy to try my hand at updating the documentation if needed. I reviewed the current patch. I can’t say whether the ast.c change is good or not. But IMO the test would be better off in somewhere like /Lib/test/test_unpack.py. It is only a superficial relationship with tuples because the syntax looks the same. Also may be worth testing that [] = [] etc work as well. |
Here is an updated patch. I moved the test into test_unpack and added additional tests. sequence -> iterable changes should probably be applied to 3.5 as well. Thanks for the review, Martin. |
Erm, I think you went overboard with the sequence → iterable changes and subscripting; see the review. Also, I think target_list should be made optional in the grammar description. |
Hi Berker. I updated your patch according to my comments in the documentation. I hope you don’t mind. I reverted all the changes about subscripting and slicing an iterable, since this bug is only about assigning to a “target list”. Actually it is true (despite the current documentation) that you can often assign sequence[slice] = iterable But I think that is a separate problem. |
New changeset 8a0754fed986 by Berker Peksag in branch 'default': |
Thanks Martin. Your edits look much better! :) I will be travelling for PyCon US later this week so I just committed issue23275_v4.diff with minor edits. |
New changeset d3a75daf61e1 by Martin Panter in branch 'default': |
I just moved the NEWS entry under the alpha 2 heading. For patches I am going to commit myself, I usually write the NEWS beforehand and remember to adjust it when committing. But perhaps I shouldn’t have done that in this case :) |
New changeset 39a72018dd76 by Martin Panter in branch '3.5': New changeset 8700f4d09b28 by Martin Panter in branch '2.7': |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: