有没有办法在解包时将 splat-assign 分配为元组而不是列表?

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

我最近惊讶地发现“splat”(一元 *)运算符在项目解包期间总是将切片捕获为 list,即使被解包的序列具有另一种类型:

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

比较这个作业在没有解包的情况下是如何写的:

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

这也与 splat 运算符在函数参数中的行为方式不一致:

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

为了在解包后将y恢复为元组,我现在必须写:

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

这仍然比基于切片的语法好得多,但仍然遭受我认为非常不必要和意外的优雅损失。有没有办法在没有 post 赋值处理的情况下将 y 恢复为元组而不是列表?

我试图通过写x, *(*y,), z = ...强制python将y解释为一个元组,但它最终还是一个列表。当然还有像 x, *tuple(y), z don't work in python.

这样愚蠢的事情

我目前正在使用 Python 3.8.3,但也欢迎 solutions/suggestions/explanations 涉及更高版本(当它们可用时)。

这是设计使然。引用关于 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).

Python 用户很可能想在之后改变您的 y,因此选择了 list 类型而不是 tuple

引用 PEP 3132 that I found through a link in 相关问题的验收部分:

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.

因此,之后使用 y = tuple(y) 进行转换是您唯一的选择。