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

时间:2010-02-09 23:07:21

标签: 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)]

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

7 个答案:

答案 0 :(得分:96)

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)

答案 1 :(得分:41)

其他出色的答案和评论很好地解释了argument unpackingzip()的作用。

正如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
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^

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

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

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

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

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

有关zip()map()http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/

的更多信息

答案 2 :(得分:28)

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

由于我们有相同的迭代器,因此它会被消耗掉并且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)]

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

答案 3 :(得分:8)

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

[iter(s)]*n列出n次与s相同的迭代器。

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

答案 4 :(得分: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:], ]

用法:

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

打印:

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

答案 5 :(得分:0)

使用python解释器或ipython的{​​{1}}中发生的事情可能更容易:

n = 2

因此,我们有两个指向相同迭代器对象的迭代器的列表。请记住,对象上的In [35]: [iter("ABCDEFGH")]*2 Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>] 返回一个迭代器对象,在这种情况下,由于iter python语法糖,它是同一迭代器两次。迭代器也只能运行一次。

此外,zip采用任意数量的可迭代项(sequencesiterables),并从每个输入序列的第i个元素创建元组。由于在我们的例子中,两个迭代器是相同的,因此zip对输出的每个2元素元组两次移动相同的迭代器。

*2

unpacking (*) operator确保迭代器用尽,在这种情况下,直到没有足够的输入来创建2元素元组为止。

这可以扩展为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. 的任何值,并且n可以按所述工作。

答案 6 :(得分:0)

我需要分解每个部分步骤以真正内化它的工作方式。我的 REPL 笔记:

>>> # refresher on using list multiples to repeat item
>>> lst = list(range(15))
>>> lst
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> # lst id value
>>> id(lst)
139755081359872
>>> [id(x) for x in [lst]*3]
[139755081359872, 139755081359872, 139755081359872]

# replacing lst with an iterator of lst
# It's the same iterator three times
>>> [id(x) for x in [iter(lst)]*3 ]
[139755085005296, 139755085005296, 139755085005296]
# without starred expression zip would only see single n-item list.
>>> print([iter(lst)]*3)
[<list_iterator object at 0x7f1b440837c0>, <list_iterator object at 0x7f1b440837c0>, <list_iterator object at 0x7f1b440837c0>]
# Must use starred expression to expand n arguments
>>> print(*[iter(lst)]*3)
<list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0>

# by repeating the same iterator, n-times,
# each pass of zip will call the same iterator.__next__() n times
# this is equivalent to manually calling __next__() until complete
>>> iter_lst = iter(lst)
>>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
(0, 1, 2)
>>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
(3, 4, 5)
>>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
(6, 7, 8)
>>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
(9, 10, 11)
>>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
(12, 13, 14)
>>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

# all together now!
# continuing with same iterator multiple times in list
>>> print(*[iter(lst)]*3)
<list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0>
>>> zip(*[iter(lst)]*3)
<zip object at 0x7f1b43f14e00>
>>> list(zip(*[iter(lst)]*3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]

# NOTE: must use list multiples. Explicit listing creates 3 unique iterators
>>> [iter(lst)]*3 == [iter(lst), iter(lst), iter(lst)]
False
>>> list(zip(*[[iter(lst), iter(lst), iter(lst)]))
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), ....