zip(*[iter(s)]*n) 在 Python 中如何工作?

问题描述 投票:0回答:10
s = [1,2,3,4,5,6,7,8,9]
n = 3

list(zip(*[iter(s)]*n)) # returns [(1,2,3),(4,5,6),(7,8,9)]

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


这是一种用于将列表分割成大小相等的块的技术 - 请参阅该问题以了解问题的一般概述。

python iterator
10个回答
130
投票

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

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

54
投票

其他很棒的答案和评论很好地解释了参数解包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
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^

既然您要求更详细的代码示例:

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

根据

start
end
的值:

[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-transforming-lists-with-map-and-zip/


35
投票

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

由于我们有相同的迭代器,它会被消耗,其余元素将由 zip 使用。所以如果我们只使用列表而不是迭代器 例如。

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,依此类推。这是一个非常微妙的事情,但是非常聪明!!!


10
投票

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

[iter(s)]*n
为 s 创建一个包含 n 次相同迭代器的列表。

因此,在执行

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


7
投票

以这种方式使用 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

7
投票

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

x = iter(s)
for a, b, c in zip(*([x] * n)):
    print(a, b, c)

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

x = iter(accounts_iter)
for a, b, c in zip(x, x, x):
    print(a, b, c)

现在应该开始变得清晰了。只有一个迭代器对象,

x
。在每次迭代中,
zip()
在幕后调用
next(x)
3 次,每个迭代器对象传递给它一次。但每次都是相同的迭代器对象。因此,它会传递前 3 个 next(x) 结果,并让共享迭代器对象等待接下来传递其第四个结果。起泡沫,冲洗,重复。
顺便说一句,我怀疑你在头脑中错误地解析了

*([iter(x)]*n)

。尾随

*n
首先发生,然后将前缀
*
应用于创建的 n 元素列表
*n
f(*iterable)
是使用可变数量的参数调用
f()
的快捷方式,每个对象对应一个
iterable
传递的参数。
    


3
投票

>>> # 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), ....



1
投票
ipython

来查看 python 解释器或

n = 2
中发生的情况可能更容易:

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

因此,我们有两个迭代器的列表,它们指向同一个迭代器对象。请记住,对象上的 
iter

返回一个迭代器对象,在这种情况下,由于

*2
python 语法糖,它是同一个迭代器两次。迭代器也只运行一次。

此外,

zip

 接受任意数量的可迭代对象(
序列可迭代对象),并从每个输入序列的第 i 个元素创建元组。由于在我们的例子中两个迭代器是相同的,因此 zip 对于输出的每个 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.

解包 (

*) 运算符

确保迭代器运行到耗尽状态,在这种情况下,直到没有足够的输入来创建 2 元素元组。
这可以扩展到

n

的任何值,并且

zip(*[iter(s)]*n)
按所述工作。
    


0
投票
等同于:

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

每次 
zip()

获取

iter_var
中的下一个值时,它都会移动到
x
的下一个值。 尝试运行
next(iter_var)
看看它是如何工作的。
    


0
投票

from itertools import zip_longest def iter(): for i in range(1, 17): print(i) yield i g = iter() m, n, o, p, q = [g] * 5 result = [*zip_longest(m, n, o, p, q, fillvalue="X")] print("returning the result:") print(result)

退货

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 returning the result: [(1, 2, 3, 4, 5), (6, 7, 8, 9, 10), (11, 12, 13, 14, 15), (16, 'X', 'X', 'X', 'X')]

	
© www.soinside.com 2019 - 2024. All rights reserved.