迭代器就像一个项目流。您一次只能查看一个流中的项目,并且您只能访问第一个元素。要查看流中的某些内容,您需要将其从流中移除,一旦您从流顶部取出某些内容,它就会从流中永久消失。
当您调用zip(i, i) 时,zip 首先查看第一个流并取出一个项目。然后它查看第二个流(恰好与第一个流相同的流)并取出一个项目。然后它从这两个项目中创建一个元组,并一遍又一遍地重复此操作,直到流中没有任何内容。
也许更容易看出我是否要在纯 python 中编写 zip 函数(为简单起见,只有 2 个参数)。它看起来像1:
def zip(a, b):
out = []
try:
while True:
item1 = next(a)
item2 = next(b)
out.append((item1, item2))
except StopIteration:
return out
现在想象一下您所讨论的情况,a 和 b 是同一个对象。在这种情况下,我们只需在迭代器上调用next 两次(在您的示例中为i),它将按顺序从i 中获取前两项并将它们打包成一个元组。
一旦我们了解了 zip(i, i) 的行为方式为何,zip(*([i] * 2)) 就不会太难了。让我们从内到外阅读表达式...
[i] * 2
这只是创建了一个新列表(长度为 2),其中两个元素都是对迭代器 i 的引用。所以它和zip(*[i, i]) 是一样的(当你想重复超过2次的时候写起来更方便)。 * unpacking 是 python 中的常用习语,您可以在 the python tutorial 中找到更多信息。它的要点是 python 获取可迭代对象并“解包”它,就好像可迭代对象的每个项目都是函数的单独位置参数一样。所以:
zip(*[i, i])
做同样的事情:
zip(i, i)
现在鲍勃是我们的叔叔了。自从zip(i, i) 是本次讨论的起点,我们才刚刚开始讨论。
1这个示例代码绝对比前面提到的只接受 2 个参数更简单。例如,zip 可能会在输入参数上调用iter,以便它适用于任何可迭代(不仅仅是迭代器),但这应该足以说明问题......