解释器何时以及为什么通过假设相同长度的子列表来解散?

时间:2019-03-15 20:36:55

标签: python flatten

令我印象深刻的是,一个简单的Python for语句可以轻松地解开列表列表,而无需numpy.unravel或等效的flatten函数。但是,现在需要权衡的是,我无法访问像这样的列表中的元素:

for a,b,c in [[5],[6],[7]]:
     print(str(a),str(b),str(c))
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 1)

,相反,此方法一直有效,直到长度为1 [5]

for a,b,c in [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]:
     print(a,b,c)

1 2 3
4 5 6
7 8 9
0 0 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 1)

从逻辑上讲,假设列表具有固定数量的元素是没有意义的。那么,Python如何允许我们假设列表列表始终具有相同数量的元素?

我想知道Python的期望,因为我想预测格式错误的列表/子列表。

我在python文档和Stackoverflow上打了个比方,但是还没有找到原因或解释器是如何做到的。

我的猜测是,平展等长数组很常见(例如,机器学习维数减少,矩阵变换等),因此可以在无法执行我所要做的事情的权衡下提供此功能已经尝试过了。

3 个答案:

答案 0 :(得分:3)

解释器始终假定在进行拆箱分配时长度是匹配的,并且如果不匹配,则以ValueError崩溃。 for循环实际上与一种“重复赋值语句”非常相似,其中LHS是循环的自由变量,而RHS是可迭代的容器,产生在每个步骤中使用的连续值迭代。

每次迭代都在循环主体的开头进行一次分配-在您的情况下,这是一个拆包分配,它绑定多个名称。

因此,为了与第二个示例完全等效,您的第一个示例为:

for a,b,c in [[5],[6],[7]]:
    ...

应改为:

for a, in [[5],[6],[7]]:
    ...

没有“期望”,也不会因为(在一般情况下)您可能要遍历任何事物,例如数据从套接字流进来。

为了完全掌握for循环流的工作方式,与赋值语句的类比非常有用。可以在赋值语句左侧使用的任何对象都可以在for循环中用作目标。例如,这等效于在字典中设置d[1] = 2等,并且应产生与dict(RHS)相同的结果:

>>> d = {}
>>> for k, d[k] in [[1, 2], [3, 4]]: 
...     pass 
...
>>> d
{1: 2, 3: 4}

这只是一堆分配好的任务。

答案 1 :(得分:3)

Python不知道,您只是告诉它通过解压缩为三个名称来期望三个元素。 ValueError说:“您告诉我们三个,但是我们发现一个没有三个元素的子可重复项,而且我们不知道该怎么办。”

Python并没有为实现此目的做任何特别的事情;除了内建类型(例如tuple(可能是list)的特殊情况外,该实现只是迭代子Iterable预期的次数并转储在解释程序堆栈中找到的所有值,然后将它们存储为提供的名称。它还尝试重复一次(期望StopIteration),这样您就不会默默地忽略多余的值。

在某些情况下,您可以通过在其中一个解压缩名称前加上*来灵活处理,以便将所有“不适合”的元素捕获到该名称中(作为list )。这样一来,您可以设置最小数量的元素,同时允许更多元素,例如如果您确实只需要第二个示例中的第一个元素,则可以执行以下操作:

for a, *_ in [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]:
    print(a,b,c)

其中_只是一个名称,按照约定,其含义是“我实际上并不关心此值,但是我需要一个占位符名称”。

另一个示例是当您需要第一个元素和最后一个元素时,但不关心中间元素:

for first, *middle, last in myiterable:
    ...

但是,否则,如果您需要处理可变长度的可迭代对象,请不要解包,只需将其存储为单个名称,然后以对程序逻辑有意义的任何方式手动对其进行迭代即可。

答案 2 :(得分:3)

Python不假定列表的长度相同,因为这不仅适用于列表。

当您迭代for a,b,c in [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]时,发生的事情是python返回了一个iterator,它将迭代(返回)每个列表值。

因此for等同于:

l = [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]

l_iter = iter(l)

a,b,c = next(l_iter)

next(l_iter)将返回列表中的每个元素,直到根据python迭代协议引发StopIteration执行。

这意味着:

a,b,c = [1,2,3]
a,b,c = [4,5,6]
a,b,c = [7,8,9]
a,b,c = [0,0,0]
a,b,c = [5]

现在您可以看到python无法将[5]解压缩为a,b,c,因为只有一个值。