如果n是k的倍数(即n % k == 0
),在Python中很容易将一个长为n的列表分成k大小的块。以下是我最喜欢的方法(直接来自文档):
>>> k = 3
>>> n = 5 * k
>>> x = range(k * 5)
>>> zip(*[iter(x)] * k)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]
(诀窍在于 [iter(x)] * k
会生成一个由 k 个指向同一迭代器的引用组成的列表,这个迭代器由 iter(x)
返回。然后 zip
调用这 k 份迭代器中的每一个恰好一次来生成每个块。在 n 不是 k 的倍数(也就是说,n % k > 0
)时,我认为这种惯用法的主要缺陷是剩下的条目被省略了,例如:)>>> zip(*[iter(x)] * (k + 1))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
有一种稍微打字时间长一点的替代方法,当 n % k == 0
时会产生与上述方法相同的结果,并且在 n % k > 0
时有更可接受的表现:
>>> map(None, *[iter(x)] * k)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]
>>> map(None, *[iter(x)] * (k + 1))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14, None)]
至少,在这里,剩下的条目被保留了下来,但最后一块用None
填充。如果只想要其他值作为填充,则itertools.izip_longest
可以解决这个问题。
但是假设需要的解决方案是最后一块不填充,即
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14)]
有没有一种简单的方法可以修改map(None, *[iter(x)]*k)
惯用语以产生这个结果?
(当然,写一个函数来解决这个问题并不难(例如,参见如何将列表均匀分成多个小块?或在Python中迭代列表的最"Pythonic"的方式是什么?等等)。因此,这个问题更准确的标题应该是“如何挽救 map(None,* [iter(x)] * k)
惯用语?”,但我认为会让很多读者迷惑。)
我被如何将列表均匀分成多个小块的简易性所吸引,并且被如何去除不需要的填充的难度(相比之下!),即使这两个问题看起来具有可比性的复杂性。