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

How does zip(*[iter(s)]*n) work in Python?

1
2
3
4
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)]

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


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

1
2
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)


另一个很好的答案和注释很好地解释了参数解包和zip()的作用。

正如Ignacio和Ujukatzel所说,传递给zip()三个对同一迭代器的引用,zip()按对迭代器的每个引用的顺序生成整数的三元组:

1
2
3
4
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
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^

由于您需要更详细的代码示例:

1
2
3
4
5
6
7
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

根据startend的值:

1
2
3
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]

fwiw,你可以用map()得到相同的结果,用None的初始参数:

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

有关zip()map()的更多信息,请访问:http://muffirnesearch.co.uk/archives/2007/10/16/python-transmoting-lists-with-map-and-zip/


我认为在所有答案中遗漏了一件事(对于熟悉迭代器的人来说可能是显而易见的),但对其他人来说却不那么明显-

因为我们有相同的迭代器,所以它被使用,其余的元素由zip使用。所以如果我们只是使用列表而不是ITER如。

1
2
3
4
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)]

使用迭代器,弹出值并只保留剩余的可用性,因此对于使用了0的zip,1可用,然后2可用,依此类推。一件非常微妙的事情,但相当聪明!!!!


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

[iter(s)]*n为s编制了一个n倍于同一迭代器的列表。

因此,在执行zip(*[iter(s)]*n)时,它按顺序从列表中的所有三个迭代器中提取一个项。因为所有的迭代器都是相同的对象,所以它只将列表分组为n的块。


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

1
2
3
4
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:], ]

用途:

1
2
for ints in n_split(range(1,12), 3):
    print ', '.join([str(i) for i in ints])

印刷品:

1
2
3
4
1, 2, 3
4, 5, 6
7, 8, 9
10, 11


在python解释器或ipythonn = 2中可能更容易看到发生了什么:

1
2
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]

所以,我们有两个迭代器的列表,它们指向同一个迭代器对象。记住,一个对象上的iter返回一个迭代器对象,在这个场景中,由于*2python的语法结构,它是同一个迭代器两次。迭代器也只运行一次。

此外,zip接受任意数量的iterables(序列是iterables),并从每个输入序列的i'th元素创建元组。在我们的例子中,由于两个迭代器都是相同的,所以对于输出的每个2元素元组,zip会移动相同的迭代器两次。

1
2
3
4
5
6
7
8
9
In [41]: help(zip)
Help on built-in function zip in module __builtin__:

zip(...)
    zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

    Return a list of tuples, where each tuple contains the i-th element
    from each of the argument sequences.  The returned list is truncated
    in length to the length of the shortest argument sequence.

解包(*操作符)确保迭代器运行到耗尽,在这种情况下,直到没有足够的输入来创建2元素元组。

这可以扩展到所述的nzip(*[iter(s)]*n)工程的任何价值。