首页 > 解决方案 > 有没有办法在解包时将 splat-assign 分配为元组而不是列表?

问题描述

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

>>> 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)

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

我试图y通过编写来强制 python 解释为一个元组x, *(*y,), z = ...,但它仍然以列表的形式结束。当然还有一些愚蠢的事情,比如x, *tuple(y), z 在 python 中不起作用

我目前使用的是 Python 3.8.3,但也欢迎涉及更高版本的解决方案/建议/解释(当它们可用时)。

标签: pythonpython-3.xlisttuplesiterable-unpacking

解决方案


这是设计使然。引用有关分配的官方文档:

...可迭代的第一项从左到右分配给加星标目标之前的目标。可迭代的最终项目分配给加星标的目标之后的目标。然后将迭代中剩余项目的列表分配给加星标的目标(列表可以为空)。

Python 用户很可能y之后想要改变你的,所以list选择了类型而不是tuple.

引用我通过此相关问题中的链接找到的PEP 3132的接受部分:

在对 python-3000 列表 [1] 进行简短讨论后,Guido 以当前形式接受了 PEP。讨论的可能变化是:

  • 只允许星号表达式作为 exprlist 中的最后一项。这将稍微简化解包代码,并允许为带星号的表达式分配一个迭代器。这种行为被拒绝了,因为它太令人惊讶了。

  • 尝试为加星标的目标赋予与源可迭代相同的类型,例如,b ina, *b = "hello"将被分配字符串"ello"。这可能看起来不错,但不可能与所有可迭代对象保持一致。

  • 使加星标的目标成为元组而不是列表。这将与函数的 一致*args,但会使结果的进一步处理更加困难。

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


推荐阅读