【发布时间】:2023-01-07 15:32:23
【问题描述】:
如何将任意长度的列表拆分为大小相等的块?
看How to iterate over a list in chunks如果数据结果直接用于循环,不需要存储。
对于带有字符串输入的相同问题,请参阅Split string every nth character?。尽管有一些变化,但通常适用相同的技术。
【问题讨论】:
-
在发布新答案之前,请考虑这个问题已经有 60 多个答案。请确保您的答案提供的信息不在现有答案中。
如何将任意长度的列表拆分为大小相等的块?
看How to iterate over a list in chunks如果数据结果直接用于循环,不需要存储。
对于带有字符串输入的相同问题,请参阅Split string every nth character?。尽管有一些变化,但通常适用相同的技术。
【问题讨论】:
这是一个生成大小均匀的块的生成器:
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i:i + n]
import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
对于 Python 2,使用 xrange 而不是 range:
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in xrange(0, len(lst), n):
yield lst[i:i + n]
下面是一个列表理解单行。不过,上面的方法更可取,因为使用命名函数可以使代码更容易理解。对于 Python 3:
[lst[i:i + n] for i in range(0, len(lst), n)]
对于 Python 2:
[lst[i:i + n] for i in xrange(0, len(lst), n)]
【讨论】:
超级简单的东西:
def chunks(xs, n):
n = max(1, n)
return (xs[i:i+n] for i in range(0, len(xs), n))
对于 Python 2,使用 xrange() 而不是 range()。
【讨论】:
len(l) or 1来处理空列表。
我知道这有点旧,但还没有人提到numpy.array_split:
import numpy as np
lst = range(50)
np.array_split(lst, 5)
结果:
[array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]
【讨论】:
直接来自(旧的)Python 文档(itertools 的食谱):
from itertools import izip, chain, repeat
def grouper(n, iterable, padvalue=None):
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
当前版本,正如 J.F.Sebastian 所建议的那样:
#from itertools import izip_longest as zip_longest # for Python 2.x
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)
def grouper(n, iterable, padvalue=None):
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)
我猜 Guido 的时间机器工作了——工作了——将会工作——将会工作——又开始工作了。
这些解决方案之所以有效,是因为[iter(iterable)]*n(或早期版本中的等效项)创建一迭代器,在列表中重复 n 次。 izip_longest 然后有效地执行“每个”迭代器的循环;因为这是同一个迭代器,所以每次这样的调用都会推进它,导致每个这样的 zip-roundrobin 生成一个 n 项的元组。
【讨论】:
我很惊讶没有人想到使用iter的two-argument form:
from itertools import islice
def chunk(it, size):
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
演示:
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
这适用于任何可迭代对象并延迟生成输出。它返回元组而不是迭代器,但我认为它仍然有一定的优雅。它也不垫;如果你想要填充,上面的一个简单的变化就足够了:
from itertools import islice, chain, repeat
def chunk_pad(it, size, padval=None):
it = chain(iter(it), repeat(padval))
return iter(lambda: tuple(islice(it, size)), (padval,) * size)
演示:
>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]
与基于 izip_longest 的解决方案一样,上面总是垫。据我所知,没有一个或两行的函数 itertools 配方可选地垫。通过结合上述两种方法,这个方法非常接近:
_no_padding = object()
def chunk(it, size, padval=_no_padding):
if padval == _no_padding:
it = iter(it)
sentinel = ()
else:
it = chain(iter(it), repeat(padval))
sentinel = (padval,) * size
return iter(lambda: tuple(islice(it, size)), sentinel)
演示:
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]
我相信这是提供可选填充的最短分块器。
作为 Tomasz Gandor observed,如果两个填充分块器遇到一长串填充值,它们将意外停止。这是以合理方式解决该问题的最终变体:
_no_padding = object()
def chunk(it, size, padval=_no_padding):
it = iter(it)
chunker = iter(lambda: tuple(islice(it, size)), ())
if padval == _no_padding:
yield from chunker
else:
for ch in chunker:
yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))
演示:
>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]
【讨论】:
这是一个适用于任意可迭代对象的生成器:
def split_seq(iterable, size):
it = iter(iterable)
item = list(itertools.islice(it, size))
while item:
yield item
item = list(itertools.islice(it, size))
例子:
>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
【讨论】:
简约而不失优雅
L = range(1, 1000)
print [L[x:x+10] for x in xrange(0, len(L), 10)]
或者如果你喜欢:
def chunks(L, n): return [L[x: x+n] for x in xrange(0, len(L), n)]
chunks(L, 10)
【讨论】:
不要重新发明轮子。
更新: 即将到来的Python 3.12 introduces itertools.batched,终于解决了这个问题。见下文。
鉴于
import itertools as it
import collections as ct
import more_itertools as mit
iterable = range(11)
n = 3
代码
list(it.batched(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
list(mit.chunked(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
list(mit.sliced(iterable, n))
# [range(0, 3), range(3, 6), range(6, 9), range(9, 11)]
list(mit.grouper(n, iterable))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
list(mit.windowed(iterable, len(iterable)//n, step=n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
list(mit.chunked_even(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
(或 DIY,如果你愿意)
标准库
list(it.zip_longest(*[iter(iterable)] * n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
d = {}
for i, x in enumerate(iterable):
d.setdefault(i//n, []).append(x)
list(d.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
dd = ct.defaultdict(list)
for i, x in enumerate(iterable):
dd[i//n].append(x)
list(dd.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
参考
more_itertools.chunked (related posted)more_itertools.slicedmore_itertools.grouper (related post)more_itertools.windowed(另见stagger、zip_offset)more_itertools.chunked_evenzip_longest(related post,related post)setdefault(排序结果需要 Python 3.6+)collections.defaultdict(排序结果需要 Python 3.6+)
+实现itertools recipes等的第三方库。 > pip install more_itertools
++包含在 Python 标准库 3.12+ 中。 batched 类似于 more_itertools.chunked。
【讨论】:
itertools.batched 是近期应该用到的解决方案,把这个答案置顶!
对我来说,“大小均匀的块”意味着它们的长度都相同,或者禁止该选项,在最小方差在长度上。例如。 21 个项目的 5 个篮子可能有以下结果:
>>> import statistics
>>> statistics.variance([5,5,5,5,1])
3.2
>>> statistics.variance([5,4,4,4,4])
0.19999999999999998
更喜欢后一种结果的一个实际原因是:如果你使用这些功能来分配工作,你已经内置了一个可能比其他人完成得更好的前景,所以它会无所事事,而其他人继续努力工作。
当我最初写这个答案时,其他答案都不是大小均匀的块——它们都在末尾留下一个矮小的块,所以它们没有很好地平衡,并且具有比必要的长度变化更高的长度。
例如,当前的最佳答案以:
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
其他人,如list(grouper(3, range(7))) 和chunk(range(7), 3) 都返回:[(0, 1, 2), (3, 4, 5), (6, None, None)]。 None 只是填充,在我看来相当不雅。他们没有均匀地分块迭代。
为什么我们不能更好地划分这些?
使用itertools.cycle 的高级平衡解决方案,这就是我今天可能会采用的方式。这是设置:
from itertools import cycle
items = range(10, 75)
number_of_baskets = 10
现在我们需要我们的列表来填充元素:
baskets = [[] for _ in range(number_of_baskets)]
最后,我们将要分配的元素与篮子的循环一起压缩,直到用完元素,从语义上讲,这正是我们想要的:
for element, basket in zip(items, cycle(baskets)):
basket.append(element)
结果如下:
>>> from pprint import pprint
>>> pprint(baskets)
[[10, 20, 30, 40, 50, 60, 70],
[11, 21, 31, 41, 51, 61, 71],
[12, 22, 32, 42, 52, 62, 72],
[13, 23, 33, 43, 53, 63, 73],
[14, 24, 34, 44, 54, 64, 74],
[15, 25, 35, 45, 55, 65],
[16, 26, 36, 46, 56, 66],
[17, 27, 37, 47, 57, 67],
[18, 28, 38, 48, 58, 68],
[19, 29, 39, 49, 59, 69]]
为了生产这个解决方案,我们编写了一个函数,并提供了类型注释:
from itertools import cycle
from typing import List, Any
def cycle_baskets(items: List[Any], maxbaskets: int) -> List[List[Any]]:
baskets = [[] for _ in range(min(maxbaskets, len(items)))]
for item, basket in zip(items, cycle(baskets)):
basket.append(item)
return baskets
在上面,我们采用了我们的项目列表和最大篮子数。我们创建一个空列表列表,以循环方式在其中附加每个元素。
另一个优雅的解决方案是使用切片——特别是不太常用的步切片的参数。 IE。:
start = 0
stop = None
step = number_of_baskets
first_basket = items[start:stop:step]
这是特别优雅的,因为切片不关心数据有多长——结果,我们的第一个篮子,只是它需要的长度。我们只需要增加每个篮子的起点。
事实上,这可能是单行代码,但我们将采用多行代码以提高可读性并避免代码行过长:
from typing import List, Any
def slice_baskets(items: List[Any], maxbaskets: int) -> List[List[Any]]:
n_baskets = min(maxbaskets, len(items))
return [items[i::n_baskets] for i in range(n_baskets)]
来自 itertools 模块的 islice 将提供一种惰性迭代方法,就像问题中最初要求的那样。
我不希望大多数用例受益太多,因为原始数据已经在列表中完全具体化,但对于大型数据集,它可以节省近一半的内存使用量。
from itertools import islice
from typing import List, Any, Generator
def yield_islice_baskets(items: List[Any], maxbaskets: int) -> Generator[List[Any], None, None]:
n_baskets = min(maxbaskets, len(items))
for i in range(n_baskets):
yield islice(items, i, None, n_baskets)
查看结果:
from pprint import pprint
items = list(range(10, 75))
pprint(cycle_baskets(items, 10))
pprint(slice_baskets(items, 10))
pprint([list(s) for s in yield_islice_baskets(items, 10)])
这是另一个平衡的解决方案,改编自我过去在生产中使用的函数,它使用模运算符:
def baskets_from(items, maxbaskets=25):
baskets = [[] for _ in range(maxbaskets)]
for i, item in enumerate(items):
baskets[i % maxbaskets].append(item)
return filter(None, baskets)
我创建了一个生成器,如果将它放入列表中,它会执行相同的操作:
def iter_baskets_from(items, maxbaskets=3):
'''generates evenly balanced baskets from indexable iterable'''
item_count = len(items)
baskets = min(item_count, maxbaskets)
for x_i in range(baskets):
yield [items[y_i] for y_i in range(x_i, item_count, baskets)]
最后,因为我看到所有上述函数都以连续的顺序返回元素(如给定的那样):
def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
'''
generates balanced baskets from iterable, contiguous contents
provide item_count if providing a iterator that doesn't support len()
'''
item_count = item_count or len(items)
baskets = min(item_count, maxbaskets)
items = iter(items)
floor = item_count // baskets
ceiling = floor + 1
stepdown = item_count % baskets
for x_i in range(baskets):
length = ceiling if x_i < stepdown else floor
yield [items.next() for _ in range(length)]
测试它们:
print(baskets_from(range(6), 8))
print(list(iter_baskets_from(range(6), 8)))
print(list(iter_baskets_contiguous(range(6), 8)))
print(baskets_from(range(22), 8))
print(list(iter_baskets_from(range(22), 8)))
print(list(iter_baskets_contiguous(range(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(range(26), 5))
print(list(iter_baskets_from(range(26), 5)))
print(list(iter_baskets_contiguous(range(26), 5)))
打印出来:
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]
请注意,连续生成器提供的块的长度模式与其他两个相同,但项目都是有序的,并且它们被均匀地划分为一个离散元素列表。
【讨论】:
def chunk(input, size):
return map(None, *([iter(input)] * size))
【讨论】:
return map(lambda *x: x, *([iter(input)] * size))。然而,如果它不能被分成相等的块,它会丢弃列表的尾部
如果您知道列表大小:
def SplitList(mylist, chunk_size):
return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]
如果你不这样做(迭代器):
def IterChunks(sequence, chunk_size):
res = []
for item in sequence:
res.append(item)
if len(res) >= chunk_size:
yield res
res = []
if res:
yield res # yield the last, incomplete, portion
在后一种情况下,如果您可以确定序列始终包含给定大小的整数块(即没有不完整的最后一个块),则可以用更漂亮的方式重新表述。
【讨论】:
我在这个问题的 duplicate 中看到了最棒的 Python-ish 答案:
from itertools import zip_longest
a = range(1, 16)
i = iter(a)
r = list(zip_longest(i, i, i))
>>> print(r)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]
您可以为任何 n 创建 n 元组。如果a = range(1, 15),那么结果将是:
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]
如果列表被平分,则可以将zip_longest替换为zip,否则三元组(13, 14, None)将丢失。上面使用的是 Python 3。对于 Python 2,请使用 izip_longest。
【讨论】:
[AA[i:i+SS] for i in range(len(AA))[::SS]]
其中 AA 是数组,SS 是块大小。例如:
>>> AA=range(10,21);SS=3
>>> [AA[i:i+SS] for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
# or [range(10, 13), range(13, 16), range(16, 19), range(19, 21)] in py3
要扩展 py3 中的范围,请执行
(py3) >>> [list(AA[i:i+SS]) for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
【讨论】:
在 Python 3.8 中使用 Assignment Expressions 会变得相当不错:
import itertools
def batch(iterable, size):
it = iter(iterable)
while item := list(itertools.islice(it, size)):
yield item
这适用于任意可迭代对象,而不仅仅是列表。
>>> import pprint
>>> pprint.pprint(list(batch(range(75), 10)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
更新
从 Python 3.12 开始,这个确切的实现可以作为 itertools.batched 获得
【讨论】:
例如,如果你的块大小为 3,你可以这样做:
zip(*[iterable[i::3] for i in range(3)])
来源: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/
当我的块大小是我可以输入的固定数字时,我会使用它,例如'3',永远不会改变。
【讨论】:
zip_longest来自itertools:docs.python.org/3/library/itertools.html#itertools.zip_longest
toolz 库为此提供了 partition 函数:
from toolz.itertoolz.core import partition
list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]
【讨论】:
我很好奇不同方法的性能,这里是:
在 Python 3.5.1 上测试
import time
batch_size = 7
arr_len = 298937
#---------slice-------------
print("
slice")
start = time.time()
arr = [i for i in range(0, arr_len)]
while True:
if not arr:
break
tmp = arr[0:batch_size]
arr = arr[batch_size:-1]
print(time.time() - start)
#-----------index-----------
print("
index")
arr = [i for i in range(0, arr_len)]
start = time.time()
for i in range(0, round(len(arr) / batch_size + 1)):
tmp = arr[batch_size * i : batch_size * (i + 1)]
print(time.time() - start)
#----------batches 1------------
def batch(iterable, n=1):
l = len(iterable)
for ndx in range(0, l, n):
yield iterable[ndx:min(ndx + n, l)]
print("
batches 1")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
tmp = x
print(time.time() - start)
#----------batches 2------------
from itertools import islice, chain
def batch(iterable, size):
sourceiter = iter(iterable)
while True:
batchiter = islice(sourceiter, size)
yield chain([next(batchiter)], batchiter)
print("
batches 2")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
tmp = x
print(time.time() - start)
#---------chunks-------------
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
print("
chunks")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in chunks(arr, batch_size):
tmp = x
print(time.time() - start)
#-----------grouper-----------
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)
def grouper(iterable, n, padvalue=None):
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)
arr = [i for i in range(0, arr_len)]
print("
grouper")
start = time.time()
for x in grouper(arr, batch_size):
tmp = x
print(time.time() - start)
结果:
slice
31.18285083770752
index
0.02184295654296875
batches 1
0.03503894805908203
batches 2
0.22681021690368652
chunks
0.019841909408569336
grouper
0.006506919860839844
【讨论】:
您也可以使用 utilspie 库的 get_chunks 函数作为:
>>> from utilspie import iterutils
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(iterutils.get_chunks(a, 5))
[[1, 2, 3, 4, 5], [6, 7, 8, 9]]
你可以通过 pip 安装 utilspie:
sudo pip install utilspie
免责声明:我是utilspie库的创建者.
【讨论】:
我非常喜欢 tzot 和 J.F.Sebastian 提出的 Python 文档版本, 但它有两个缺点:
我在我的代码中经常使用这个:
from itertools import islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield tuple(islice(iterable, n)) or iterable.next()
更新:惰性块版本:
from itertools import chain, islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield chain([next(iterable)], islice(iterable, n-1))
【讨论】:
代码:
def split_list(the_list, chunk_size):
result_list = []
while the_list:
result_list.append(the_list[:chunk_size])
the_list = the_list[chunk_size:]
return result_list
a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print split_list(a_list, 3)
结果:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
【讨论】:
呵呵,单行版
In [48]: chunk = lambda ulist, step: map(lambda i: ulist[i:i+step], xrange(0, len(ulist), step))
In [49]: chunk(range(1,100), 10)
Out[49]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
[31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
[51, 52, 53, 54, 55, 56, 57, 58, 59, 60],
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70],
[71, 72, 73, 74, 75, 76, 77, 78, 79, 80],
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
[91, 92, 93, 94, 95, 96, 97, 98, 99]]
【讨论】:
def chunk 而不是 chunk=lambda 产生的函数对象具有 .__name__ 属性 'chunk' 而不是 '<lambda>'。具体名称在回溯中更有用。
另一个更明确的版本。
def chunkList(initialList, chunkSize):
"""
This function chunks a list into sub lists
that have a length equals to chunkSize.
Example:
lst = [3, 4, 9, 7, 1, 1, 2, 3]
print(chunkList(lst, 3))
returns
[[3, 4, 9], [7, 1, 1], [2, 3]]
"""
finalList = []
for i in range(0, len(initialList), chunkSize):
finalList.append(initialList[i:i+chunkSize])
return finalList
【讨论】:
在这一点上,我认为我们需要一个递归生成器, 万一...
在蟒蛇 2 中:
def chunks(li, n):
if li == []:
return
yield li[:n]
for e in chunks(li[n:], n):
yield e
在蟒蛇 3 中:
def chunks(li, n):
if li == []:
return
yield li[:n]
yield from chunks(li[n:], n)
此外,如果发生大规模外星人入侵,装饰递归生成器可能会变得方便:
def dec(gen):
def new_gen(li, n):
for e in gen(li, n):
if e == []:
return
yield e
return new_gen
@dec
def chunks(li, n):
yield li[:n]
for e in chunks(li[n:], n):
yield e
【讨论】:
不调用 len() 这对大列表有好处:
def splitter(l, n):
i = 0
chunk = l[:n]
while chunk:
yield chunk
i += n
chunk = l[i:i+n]
这是针对可迭代对象的:
def isplitter(l, n):
l = iter(l)
chunk = list(islice(l, n))
while chunk:
yield chunk
chunk = list(islice(l, n))
上面的功能风味:
def isplitter2(l, n):
return takewhile(bool,
(tuple(islice(start, n))
for start in repeat(iter(l))))
或者:
def chunks_gen_sentinel(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return iter(imap(tuple, continuous_slices).next,())
或者:
def chunks_gen_filter(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return takewhile(bool,imap(tuple, continuous_slices))
【讨论】:
len();这是一个恒定时间的操作。
def split_seq(seq, num_pieces):
start = 0
for i in xrange(num_pieces):
stop = start + len(seq[i::num_pieces])
yield seq[start:stop]
start = stop
用法:
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for seq in split_seq(seq, 3):
print seq
【讨论】:
>>> orange = range(1, 1001)
>>> otuples = list( zip(*[iter(orange)]*10))
>>> print(otuples)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ... (991, 992, 993, 994, 995, 996, 997, 998, 999, 1000)]
>>> olist = [list(i) for i in otuples]
>>> print(olist)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
>>>
Python3
【讨论】:
zip(*[iter(range(7))]*3) 仅返回 [(0, 1, 2), (3, 4, 5)] 并忘记输入中的 6。
def chunks(iterable,n):
"""assumes n is an integer>0
"""
iterable=iter(iterable)
while True:
result=[]
for i in range(n):
try:
a=next(iterable)
except StopIteration:
break
else:
result.append(a)
if result:
yield result
else:
break
g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'
【讨论】:
因为这里的每个人都在谈论迭代器。 boltons 有完美的方法,称为 iterutils.chunked_iter。
from boltons import iterutils
list(iterutils.chunked_iter(list(range(50)), 11))
输出:
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
[22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[44, 45, 46, 47, 48, 49]]
但是如果你不想怜悯内存,你可以使用旧方法并首先存储完整的list和iterutils.chunked。
【讨论】:
考虑使用matplotlib.cbook件
例如:
import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
print s
【讨论】:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
CHUNK = 4
[a[i*CHUNK:(i+1)*CHUNK] for i in xrange((len(a) + CHUNK - 1) / CHUNK )]
【讨论】: