zip(*[iter(s)]*n)如何在Python中运行?

Oli*_*eng 93 python iterator

s = [1,2,3,4,5,6,7,8,9]
n = 3

zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Run Code Online (Sandbox Code Playgroud)

zip(*[iter(s)]*n)工作怎么样?如果用更详细的代码编写它会是什么样子?

Ign*_*ams 98

iter()是序列上的迭代器.[x] * n生成一个包含n数量的列表x,即n每个元素所在的长度列表x.*arg将序列解包为函数调用的参数.因此,您将相同的迭代器传递3次zip(),并且每次都从迭代器中提取一个项目.

x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
Run Code Online (Sandbox Code Playgroud)

  • **很高兴知道:**当迭代器`yield`s(= `return`s)一个项目时,您可以将这个项目想象为“已消耗”。所以下一次调用迭代器时,它会产生下一个“未消耗”的项目。 (4认同)

ber*_*nie 43

其他很好的答案和评论很好地解释了参数解包zip()的作用.

正如Ignacioujukatzel所说,你传递给zip()同一个迭代器的三个引用,并按zip()顺序从每个引用到迭代器生成3个元组的整数:

1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^
Run Code Online (Sandbox Code Playgroud)

因为你要求更详细的代码示例:

chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]

# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
    end = start + chunk_size
    print L[start:end] # three-item chunks
Run Code Online (Sandbox Code Playgroud)

下面的值startend:

[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
Run Code Online (Sandbox Code Playgroud)

FWIW,您可以map()使用以下初始参数获得相同的结果None:

>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Run Code Online (Sandbox Code Playgroud)

欲了解更多信息zip(),请访问map():http: //muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/


gab*_*jit 29

我认为在所有答案中都缺少一件事(对熟悉迭代器的人来说可能很明显)但对其他人来说并不那么明显 -

由于我们有相同的迭代器,因此它会被消耗掉并且zip使用其余的元素.所以,如果我们只使用列表而不是iter,例如.

l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate 
# output 
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]
Run Code Online (Sandbox Code Playgroud)

使用迭代器,弹出值并仅保持剩余可用,因此对于zip,一旦消耗0,则可用1,然后是2,依此类推.一个非常微妙的事情,但相当聪明!


stt*_*ter 8

iter(s) 返回s的迭代器.

[iter(s)]*n 为s创建n次相同迭代器的列表.

因此,在执行操作时zip(*[iter(s)]*n),它会按顺序从列表中的所有三个迭代器中提取项目.由于所有迭代器都是同一个对象,因此它只是按照块的形式对列表进行分组n.

  • 不是"同一列表的迭代器",而是"n次相同的迭代器对象".不同的迭代器对象不共享状态,即使它们属于同一个列表. (6认同)

Tim*_*ers 7

展开“聪明”的层层,你可能会发现这个等效的拼写更容易理解:

x = iter(s)
for a, b, c in zip(*([x] * n)):
    print(a, b, c)
Run Code Online (Sandbox Code Playgroud)

反过来,这相当于更不聪明的:

x = iter(accounts_iter)
for a, b, c in zip(x, x, x):
    print(a, b, c)
Run Code Online (Sandbox Code Playgroud)

现在事情应该开始变得清楚了。只有一个迭代器对象,x。在每次迭代中,zip(),在幕后调用next(x)3 次,每个迭代器对象传递给它一次。但每次都是相同的迭代器对象。因此,它传递前 3 个next(x)结果,并让共享迭代器对象等待接下来传递其第四个结果。起泡沫,冲洗,重复。

顺便说一句,我怀疑你*([iter(x)]*n)的头脑解析不正确。首先发生尾随*n,然后将前缀*应用于*n创建的 n 元素列表。f(*iterable)是一种f()使用可变数量的参数进行调用的快捷方式,每个对象iterable传递一个参数。


jma*_*son 5

用这种方式使用zip的建议之一.如果列表的长度不可分割,它将截断列表.要解决此问题,您可以使用itertools.izip_longest,如果您可以接受填充值.或者你可以使用这样的东西:

def n_split(iterable, n):
    num_extra = len(iterable) % n
    zipped = zip(*[iter(iterable)] * n)
    return zipped if not num_extra else zipped + [iterable[-num_extra:], ]
Run Code Online (Sandbox Code Playgroud)

用法:

for ints in n_split(range(1,12), 3):
    print ', '.join([str(i) for i in ints])
Run Code Online (Sandbox Code Playgroud)

打印:

1, 2, 3
4, 5, 6
7, 8, 9
10, 11
Run Code Online (Sandbox Code Playgroud)

  • 这已在`itertools`食谱中记录:http://docs.python.org/2/library/itertools.html#recipes`pubper`.无需重新发明轮子 (3认同)