Is there a way to splat-assign as tuple instead of list when unpacking?
Asked Answered
M

1

13

I was recently surprised to find that the "splat" (unary *) operator always captures slices as a list during item unpacking, even when the sequence being unpacked has another type:

>>> x, *y, z = tuple(range(5))
>>> y
[1, 2, 3]  # list, was expecting tuple

Compare to how this assignment would be written without unpacking:

>>> my_tuple = tuple(range(5))
>>> x = my_tuple[0]
>>> y = my_tuple[1:-1]
>>> z = my_tuple[-1]
>>> y
(1, 2, 3)

It is also inconsistent with how the splat operator behaves in function arguments:

>>> def f(*args):
...     return args, type(args)
...
>>> f()
((), <class 'tuple'>)

In order to recover y as a tuple after unpacking, I now have to write:

>>> x, *y, z = tuple(range(5))
>>> y = tuple(y)

Which is still much better that the slice-based syntax, but nonetheless suffers from what I consider to be a very unnecessary and unexpected loss of elegance. Is there any way to recover y as a tuple instead of a list without post-assignment processing?

I tried to force python to interpret y as a tuple by writing x, *(*y,), z = ..., but it still ended up as a list. And of course silly things like x, *tuple(y), z don't work in python.

I am currently using Python 3.8.3 but solutions/suggestions/explanations involving higher versions (as they become available) are also welcome.

Micelle answered 24/4, 2020 at 0:35 Comment(5)
If you just pretend that y is a tuple, does this limit you in any way? As far as I can tell, in duck-typing fashion, anywhere a tuple is expected, you can substitute it with a list.Sandwich
@Sandwich Lists cannot be added to sets or used as dictionary keys. Tuples can. That's how I discovered this behavior in the first place (error when used as a dictionary key).Micelle
That's right, I didn't consider that.Sandwich
But tuple is hashable,and it can't be changed.The purpose of design may consider that.If you want to use it as the keys in dictionary,why don't you change it to tuple when you define the dictionary?Basin
As a side-note: x, *y, z = tuple(range(5)) is equivalent to x, *y, z = range(5), so you could at least avoid repeating tuple twice by dropping it from the first statement.Arching
K
5

This is by design. Quoting the official docs about Assignment:

...The first items of the iterable are assigned, from left to right, to the targets before the starred target. The final items of the iterable are assigned to the targets after the starred target. A list of the remaining items in the iterable is then assigned to the starred target (the list can be empty).

It is highly probable that the Python user wants to mutate your y afterwards, so the list type was chosen over the tuple.

Quoting the Acceptance section of PEP 3132 that I found through a link in this related question:

After a short discussion on the python-3000 list [1], the PEP was accepted by Guido in its current form. Possible changes discussed were:

  • Only allow a starred expression as the last item in the exprlist. This would simplify the unpacking code a bit and allow for the starred expression to be assigned an iterator. This behavior was rejected because it would be too surprising.

  • Try to give the starred target the same type as the source iterable, for example, b in a, *b = "hello" would be assigned the string "ello". This may seem nice, but is impossible to get right consistently with all iterables.

  • Make the starred target a tuple instead of a list. This would be consistent with a function's *args, but make further processing of the result harder.

So converting with y = tuple(y) afterwards is your only option.

Kelso answered 7/5, 2020 at 19:21 Comment(4)
This does seem to answer the core of my question, i.e., "is there a way to splat-assign as as a tuple" --> "no."Micelle
I do not understand the downvote, but If it is because of the lack of references, I actually found the link and edited the answer.Kelso
I don't know why you got downvoted. You may want to quote the last two bullets in he pep. They address the exact reason OP is looking forFreedafreedman
Yes, this link contains the explanation I am looking for. I will accept this answer if you replace your supposition about it being "on purpose" with the text of the last two bullet points.Micelle

© 2022 - 2024 — McMap. All rights reserved.