当n%k> 1时,将n长列表分成k长块的简单成语0?

时间:2011-08-10 02:44:55

标签: python itertools map-function

在Python中,如果 n n -long列表分成 k -size块> k (IOW,n % k == 0)。这是我最喜欢的方法(直接来自docs):

>>> 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)返回。然后{{ 1}}通过调用迭代器的每个 k 副本一次生成每个块。zip之前的*是必要的,因为[iter(x)] * k期望接收它参数为“单独的”迭代器,而不是它们的列表。)

我用这个习语看到的主要缺点是,当 n 不是 k (IOW,zip)的倍数时,左边的条目刚被排除在外; e.g:

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)] 填充。如果只想为填充设置不同的值,那么itertools.izip_longest可以解决问题。

但是假设所需的解决方案是最后一个块没有填充,即

None

是否有简单的方法来修改[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14)] 成语以产生此结果?

(当然,通过编写函数来解决这个问题并不困难(例如,参见对How do you split a list into evenly sized chunks?What is the most "pythonic" way to iterate over a list in chunks?的许多精细回复)。因此,更准确的标题为此问题是“如何挽救map(None, *[iter(x)]*k)成语?”,但我认为这会让很多读者感到困惑。)

令我震惊的是,将列表分成大小合适是多么容易,并且有多难(相比之下!)除去不需要的填充,即使这两个问题似乎相当复杂。

4 个答案:

答案 0 :(得分:15)

[x[i:i+k] for i in range(0,n,k)]

答案 1 :(得分:3)

sentinal = object()
split = ( 
    (v for v in r if v is not sentinal) for r in
    izip_longest(*[iter(x)]*n, fillvalue=sentinal))

当然,更好的习惯是调用一个函数,因为它会比那些做同样事情的东西更具可读性。

答案 2 :(得分:3)

来自IPython的来源:

def chop(seq,size):
    """Chop a sequence into chunks of the given size."""
    chunk = lambda i: seq[i:i+size]
    return map(chunk,xrange(0,len(seq),size))

如果序列不能被完全整除,则返回的最后一个列表将少于chunk个元素,基本上它会得到棒的短端但没有抱怨。

>>> chop(range(12),3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
>>> chop(range(12),4)
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
>>> chop(range(12),5)
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11]]
>>> chop(range(12),6)
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]]

答案 3 :(得分:1)

这个怎么样?这是一个不同的习语,但会产生你想要的结果:

[x[i:i+k] for i in range(0,len(x),k)] #=> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]]
[x[i:i+k] for i in range(0,len(x),k)] #=> [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14]]

或者,如果您确实需要元组,请使用tuple(x[i:i+k])而不是x[i:i+k]