12

在 Python 中,如果nk (IOW, ) 的倍数,则很容易将n长的列表分成k大小的块。这是我最喜欢的方法(直接来自文档):n % k == 0

>>> 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引用的列表,由 . 返回。然后通过调用迭代器的每个k个副本恰好一次来生成每个块。之前是必要的,因为期望将其参数作为“单独" 迭代器,而不是它们的列表。)iter(x)zip*[iter(x)] * kzip

我看到这个习语的主要缺点是,当n不是k (IOW, 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)成语来产生这个结果?

(当然,通过编写一个函数来解决这个问题并不难(例如,参见对如何将列表拆分为大小均匀的块?什么是最“pythonic”的迭代分块列出?)。因此,这个问题的更准确的标题是“如何挽救map(None, *[iter(x)]*k)成语?”,但我认为这会让很多读者感到困惑。)

我惊讶于将列表分成大小均匀的块是多么容易,以及摆脱不需要的填充是多么困难(相比之下!),即使这两个问题看起来具有相当的复杂性。

4

4 回答 4

15
[x[i:i+k] for i in range(0,n,k)]
于 2011-08-10T03:01:17.157 回答
3
sentinal = object()
split = ( 
    (v for v in r if v is not sentinal) for r in
    izip_longest(*[iter(x)]*n, fillvalue=sentinal))

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

于 2011-08-10T03:01:19.263 回答
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]]
于 2011-08-10T08:05:31.057 回答
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])而不是 just x[i:i+k]

于 2011-08-10T03:05:13.067 回答