如果您想将列表切片,可以使用以下技巧:
list_of_slices = zip(*(iter(the_list),) * slice_size)
例如。
>>> zip(*(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
如果列表的项数不能被切片大小整除,并且您想使用None填充列表,您可以执行以下操作:
>>> map(None, *(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
这是一个不太光彩的小技巧。
好的,我来解释一下它是如何工作的。虽然解释起来可能有点棘手,但我会尽力的。
首先讲一下背景:
在Python中,你可以通过以下方式将一个列表乘以一个数字:
[1, 2, 3] * 3 -> [1, 2, 3, 1, 2, 3, 1, 2, 3]
([1, 2, 3],) * 3 -> ([1, 2, 3], [1, 2, 3], [1, 2, 3])
迭代器对象可以像这样被消耗掉一次:
And an iterator object can be consumed once like this:
>>> l=iter([1, 2, 3])
>>> l.next()
1
>>> l.next()
2
>>> l.next()
3
zip函数返回一个元组列表,第i个元组包含每个参数序列或可迭代对象中第i个元素。例如:
zip([1, 2, 3], [20, 30, 40]) -> [(1, 20), (2, 30), (3, 40)]
zip(*[(1, 20), (2, 30), (3, 40)]) -> [[1, 2, 3], [20, 30, 40]]
在zip前面的星号用于解包参数。你可以在这里找到更多详细信息。
zip(*[(1, 20), (2, 30), (3, 40)])
实际上等同于
zip((1, 20), (2, 30), (3, 40))
但是可以使用可变数量的参数。
现在回到这个技巧:
list_of_slices = zip(*(iter(the_list),) * slice_size)
iter(the_list)
-> 将列表转换为迭代器。
(iter(the_list),) * N
-> 会生成 N 个对 the_list 迭代器的引用。
zip(*(iter(the_list),) * N)
-> 将这些迭代器列表传递给 zip 函数,zip 函数将把它们分组成大小为 N 的元组。但是由于所有 N 个项实际上都是对同一个迭代器 iter(the_list)
的引用,所以结果将重复调用原始迭代器上的 next()
。
希望这能解释清楚。我建议您选择更易理解的解决方案。我只是想提及这个技巧,因为我喜欢它。