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)
如何工作?如果使用更冗长的代码编写,会是什么样?#1 楼
iter()
是序列上的迭代器。 [x] * n
产生一个包含n
数量的x
的列表,即长度为n
的列表,其中每个元素为x
。 *arg
将序列分解为函数调用的参数。因此,您要将同一迭代器传递给zip()
3次,并且每次都会从迭代器中提取一个项目。x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
评论
必知的事情:当迭代器产生(=返回)一个项目时,您可以想象该项目为“已消耗”。因此,下次调用迭代器时,它将产生下一个“未消耗”的项目。
–winklerrr
19年4月23日在11:14
#2 楼
其他很棒的答案和注释很好地解释了参数解压缩和zip()的作用。正如Ignacio和ujukatzel所说,您将对同一个迭代器的三个引用传递给
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-transposed-lists-with-map -and-zip / #3 楼
我认为所有答案中都漏掉了一件事(对熟悉迭代器的人来说可能很明显),而对其他人却不那么明显-因为我们有相同的迭代器,所以它被消耗了,其余元素被消耗掉了由拉链使用。因此,如果我们仅使用列表而不使用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个,依此类推。一件非常微妙的事情,但是非常聪明!!!
评论
+1,您救了我!我不敢相信,假设每个人都知道,其他答案就忽略了这个重要的细节。您是否可以参考包含此信息的文档?
–Snehasish Karmakar
16-10-6在19:27
#4 楼
iter(s)
返回s的迭代器。[iter(s)]*n
列出s的相同迭代器的n倍。因此,当执行
zip(*[iter(s)]*n)
时,它从所有三个迭代器中提取一项列表中的迭代器。由于所有迭代器都是同一个对象,因此它将列表按n
的大块进行分组。评论
不是“同一列表的n个迭代器”,而是“ n次相同的迭代器对象”。不同的迭代器对象即使在相同列表中也不会共享状态。
–托马斯·沃特斯
2010-2-9在23:41
谢谢,改正。确实,那是我在“思考”的东西,但是写了其他东西。
–sttwister
2010-2-10在19:44
#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
评论
在itertools食谱中已对此进行了记录:docs.python.org/2/library/itertools.html#recipes grouper。无需重新发明轮子
– Jamylak
13年4月16日在7:05
#6 楼
可能更容易看到在python解释器或带有ipython
的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)
可以按所述工作。评论
很抱歉,速度太慢。但是您能否解释一下“由于* 2 python语法糖,相同的迭代器两次。迭代器也只运行一次。”请分开吗?如果是这样,结果为什么不是[(“ A”,“ A”)....]?谢谢。
–刘伯文
2月28日,2:13
@BowenLiu *只是方便地复制对象。尝试使用标量,然后使用列表。还可以尝试使用print(* zip(* [iter(“ ABCDEFG”)] * 2))vs print(* zip(* [iter(“ ABCDEFG”),iter(“ ABCDEFG”)])))。然后,将它们分解成更小的步骤,以查看两个语句中实际的迭代器对象是什么。
– akhan
2月28日18:26
评论
还可以在这里了解它的工作原理:stackoverflow.com/questions/2202461/…如果这里的答案还不够,我在这里写了博客:telliott99.blogspot.com/2010/01/…
尽管非常有趣,但是该技术必须与Python的核心“可读性”值背道而驰!