关于python:什么是最好的方法来对列表进行分块迭代?

What is the most “pythonic” way to iterate over a list in chunks?

我有一个python脚本,它将一个整数列表作为输入,我需要一次处理四个整数。不幸的是,我没有对输入的控制权,或者将它作为四元素元组的列表传入。目前,我以这种方式迭代它:

1
2
3
for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

不过,它看起来很像"c-think",这让我怀疑有一种更为Python式的方式来处理这种情况。列表在迭代后被丢弃,因此不需要保留它。也许这样更好?

1
2
3
while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

不过,还是感觉不太对。- -

相关问题:如何在Python中将列表分割成大小均匀的块?


1
2
3
def chunker(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# (in python 2 use xrange() instead of range() to avoid allocating a list)

简单。容易的。快。使用任何序列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
text ="I am a very, very helpful text"

for group in chunker(text, 7):
   print repr(group),
# 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt'

print '|'.join(chunker(text, 10))
# I am a ver|y, very he|lpful text

animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish']

for group in chunker(animals, 3):
    print group
# ['cat', 'dog', 'rabbit']
# ['duck', 'bird', 'cow']
# ['gnu', 'fish']


从python的itertools文档的"配方"部分修改:

1
2
3
4
5
from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

例子在伪代码中保持示例的简洁。

1
grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'

注意:在python 2上,使用izip_longest而不是zip_longest


我是个迷

1
2
3
4
chunk_size= 4
for i in range(0, len(ints), chunk_size):
    chunk = ints[i:i+chunk_size]
    # process chunk of size <= chunk_size


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import itertools
def chunks(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# though this will throw ValueError if the length of ints
# isn't a multiple of four:
for x1,x2,x3,x4 in chunks(ints,4):
    foo += x1 + x2 + x3 + x4

for chunk in chunks(ints,4):
    foo += sum(chunk)

另一种方式:

1
2
3
4
5
6
7
8
9
10
11
12
import itertools
def chunks2(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# x2, x3 and x4 could get the value 0 if the length is not
# a multiple of 4.
for x1,x2,x3,x4 in chunks2(ints,4,0):
    foo += x1 + x2 + x3 + x4


1
2
3
4
from itertools import izip_longest

def chunker(iterable, chunksize, filler):
    return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)


我需要一个解决方案,也可以与发电机组和发电机一起工作。我想不出任何非常短和漂亮的东西,但至少它是可读的。

1
2
3
4
5
6
7
8
9
def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

名单:

1
2
>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

设置:

1
2
>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

发电机:

1
2
>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]


这个问题的理想解决方案是使用迭代器(而不仅仅是序列)。它也应该很快。

这是ITertools文档提供的解决方案:

1
2
3
4
def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

使用ipython的%timeit在我的Mac Book Air上,我得到每个环路47.5美元。

然而,这对我真的不起作用,因为结果被填充成大小相等的组。没有填充的解决方案稍微复杂一些。最天真的解决方案可能是:

1
2
3
4
5
6
7
8
9
10
11
12
def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

简单,但速度很慢:每个回路693 us

我能想到的最好的解决方案是使用islice作为内环:

1
2
3
4
5
6
7
def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

使用相同的数据集,每个循环获得305 us。

我无法以更快的速度获得纯解决方案,我提供了以下解决方案,但需要注意的是:如果您的输入数据中有filldata的实例,则可能会得到错误的答案。

1
2
3
4
5
6
7
8
def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if tuple(i)[-1] == fillvalue:
            yield tuple(v for v in i if v != fillvalue)
        else:
            yield i

我真的不喜欢这个答案,但它明显更快。每环124美


与其他建议类似,但不完全相同,我喜欢这样做,因为它简单易读:

1
2
3
4
5
6
it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in zip(it, it, it, it):
    print chunk

>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)

这样就不会得到最后的部分块。如果你想把(9, None, None, None)作为最后一个块,只需使用itertools中的izip_longest


由于还没有人提到它,这里有一个zip()解决方案:

1
2
>>> def chunker(iterable, chunksize):
...     return zip(*[iter(iterable)]*chunksize)

只有当序列的长度总是可以被块大小整除,或者不关心尾随的块(如果不是尾随的块)时,它才起作用。

例子:

1
2
3
4
5
6
7
>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9')]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8')]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

或者使用itertools.izip返回迭代器而不是列表:

1
2
3
>>> from itertools import izip
>>> def chunker(iterable, chunksize):
...     return izip(*[iter(iterable)]*chunksize)

可以使用@932;_Ω__磴_的答案来固定衬垫:

1
2
3
4
5
>>> from itertools import chain, izip, repeat
>>> def chunker(iterable, chunksize, fillvalue=None):
...     it   = chain(iterable, repeat(fillvalue, chunksize-1))
...     args = [it] * chunksize
...     return izip(*args)


使用map()而不是zip()修复了j.f.Sebastian答案中的填充问题:

1
2
>>> def chunker(iterable, chunksize):
...   return map(None,*[iter(iterable)]*chunksize)

例子:

1
2
3
4
5
6
7
>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9'), ('0', None, None)]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8'), ('9', '0', None, None)]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]


如果您不介意使用外部包,您可以使用来自iteration_utilties1的iteration_utilities.grouper。它支持所有iterables(不仅仅是序列):

1
2
3
4
from iteration_utilities import grouper
seq = list(range(20))
for group in grouper(seq, 4):
    print(group)

哪些印刷品:

1
2
3
4
5
(0, 1, 2, 3)
(4, 5, 6, 7)
(8, 9, 10, 11)
(12, 13, 14, 15)
(16, 17, 18, 19)

如果长度不是GroupSize的倍数,它还支持填充(不完整的最后一个组)或截断(丢弃不完整的最后一个组)最后一个组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from iteration_utilities import grouper
seq = list(range(17))
for group in grouper(seq, 4):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16,)

for group in grouper(seq, 4, fillvalue=None):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16, None, None, None)

for group in grouper(seq, 4, truncate=True):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)

1免责声明:我是那个包裹的作者。


如果列表较大,则执行此操作的最高方法是使用生成器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_chunk(iterable, chunk_size):
    result = []
    for item in iterable:
        result.append(item)
        if len(result) == chunk_size:
            yield tuple(result)
            result = []
    if len(result) > 0:
        yield tuple(result)

for x in get_chunk([1,2,3,4,5,6,7,8,9,10], 3):
    print x

(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10,)


使用一些小的函数和东西对我来说确实不吸引人;我更喜欢只使用切片:

1
2
3
4
5
data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
    ...


另一种方法是使用iter的两种论证形式:

1
2
3
4
5
from itertools import islice

def group(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

这可以很容易地适应使用填充(这类似于Markus Jarderot的回答):

1
2
3
4
5
from itertools import islice, chain, repeat

def group_pad(it, size, pad=None):
    it = chain(iter(it), repeat(pad))
    return iter(lambda: tuple(islice(it, size)), (pad,) * size)

这些甚至可以组合用于可选填充:

1
2
3
4
5
6
7
8
9
_no_pad = object()
def group(it, size, pad=_no_pad):
    if pad == _no_pad:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(pad))
        sentinel = (pad,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)


有了numpy,很简单:

1
2
3
ints = array([1, 2, 3, 4, 5, 6, 7, 8])
for int1, int2 in ints.reshape(-1, 2):
    print(int1, int2)

输出:

1
2
3
4
1 2
3 4
5 6
7 8

下面是一个不带导入的chunker,它支持生成器:

1
2
3
4
5
6
7
8
def chunks(seq, size):
    it = iter(seq)
    while True:
        ret = tuple(next(it) for _ in range(size))
        if len(ret) == size:
            yield ret
        else:
            raise StopIteration()

使用实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def foo():
...     i = 0
...     while True:
...         i += 1
...         yield i
...
>>> c = chunks(foo(), 3)
>>> c.next()
(1, 2, 3)
>>> c.next()
(4, 5, 6)
>>> list(chunks('abcdefg', 2))
[('a', 'b'), ('c', 'd'), ('e', 'f')]

在您的第二种方法中,我将通过这样做前进到下一组4:

1
ints = ints[4:]

但是,我没有做任何性能度量,所以我不知道哪一个更有效。

既然如此,我通常会选择第一种方法。这并不漂亮,但这往往是与外界交往的结果。


我从不希望我的大块被填充,所以这一要求是必要的。我发现在任何不可测的地方工作的能力也是必要的。考虑到这一点,我决定扩展接受的答案:https://stackoverflow.com/a/434411/1074659。

如果由于需要比较和过滤填充值而不需要填充,那么在这种方法中性能会受到轻微影响。但是,对于大的块大小,这个实用程序的性能非常好。

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
#!/usr/bin/env python3
from itertools import zip_longest


_UNDEFINED = object()


def chunker(iterable, chunksize, fillvalue=_UNDEFINED):
   """
    Collect data into chunks and optionally pad it.

    Performance worsens as `chunksize` approaches 1.

    Inspired by:
        https://docs.python.org/3/library/itertools.html#itertools-recipes

   """

    args = [iter(iterable)] * chunksize
    chunks = zip_longest(*args, fillvalue=fillvalue)
    yield from (
        filter(lambda val: val is not _UNDEFINED, chunk)
        if chunk[-1] is _UNDEFINED
        else chunk
        for chunk in chunks
    ) if fillvalue is _UNDEFINED else chunks

我喜欢这种方法。它感觉简单而不神奇,支持所有无法识别的类型,不需要进口。

1
2
3
4
5
6
7
def chunk_iter(iterable, chunk_size):
it = iter(iterable)
while True:
    chunk = tuple(next(it) for _ in range(chunk_size))
    if not chunk:
        break
    yield chunk


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def group_by(iterable, size):
   """Group an iterable into lists that don't exceed the size given.

    >>> group_by([1,2,3,4,5], 2)
    [[1, 2], [3, 4], [5]]

   """

    sublist = []

    for index, item in enumerate(iterable):
        if index > 0 and index % size == 0:
            yield sublist
            sublist = []

        sublist.append(item)

    if sublist:
        yield sublist


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def chunker(iterable, n):
   """Yield iterable in chunk sizes.

    >>> chunks = chunker('ABCDEF', n=4)
    >>> chunks.next()
    ['A', 'B', 'C', 'D']
    >>> chunks.next()
    ['E', 'F']
   """

    it = iter(iterable)
    while True:
        chunk = []
        for i in range(n):
            try:
                chunk.append(next(it))
            except StopIteration:
                yield chunk
                raise StopIteration
        yield chunk

if __name__ == '__main__':
    import doctest

    doctest.testmod()

关于J.F. Sebastian给出的解决方案:

1
2
def chunker(iterable, chunksize):
    return zip(*[iter(iterable)]*chunksize)

它很聪明,但有一个缺点——总是返回tuple。如何获取字符串?当然,您可以编写''.join(chunker(...)),但是临时元组无论如何都是被构造的。

您可以通过编写自己的zip来摆脱临时元组,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class IteratorExhausted(Exception):
    pass

def translate_StopIteration(iterable, to=IteratorExhausted):
    for i in iterable:
        yield i
    raise to # StopIteration would get ignored because this is generator,
             # but custom exception can leave the generator.

def custom_zip(*iterables, reductor=tuple):
    iterators = tuple(map(translate_StopIteration, iterables))
    while True:
        try:
            yield reductor(next(i) for i in iterators)
        except IteratorExhausted: # when any of iterators get exhausted.
            break

然后

1
2
def chunker(data, size, reductor=tuple):
    return custom_zip(*[iter(data)]*size, reductor=reductor)

示例用法:

1
2
3
4
5
6
7
8
9
10
>>> for i in chunker('12345', 2):
...     print(repr(i))
...
('1', '2')
('3', '4')
>>> for i in chunker('12345', 2, ''.join):
...     print(repr(i))
...
'12'
'34'


另一个答案是:

1)容易理解2)适用于任何不可重复的,而不仅仅是序列(上面的一些答案会扼杀文件句柄)3)不同时将块加载到内存中4)不会在内存中列出对同一迭代器的长块引用列表5)列表末尾没有填充填充值

尽管如此,我还没有对它进行计时,因此它可能比一些更聪明的方法要慢,并且考虑到用例,一些优势可能是不相关的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def chunkiter(iterable, size):
  def inneriter(first, iterator, size):
    yield first
    for _ in xrange(size - 1):
      yield iterator.next()
  it = iter(iterable)
  while True:
    yield inneriter(it.next(), it, size)

In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:                                                
          for c in ii:
            print c,
          print ''
        ...:    
        a b c
        d e f
        g h

更新:由于内部循环和外部循环从同一迭代器中提取值这一事实,有两个缺点:1)Continue在外循环中不能按预期工作-它只继续下一个项目,而不是跳过一个块。但是,这似乎不是一个问题,因为在外部循环中没有任何要测试的内容。2)break在内部循环中不能按预期工作-控制将在内部循环中再次结束,迭代器中的下一个项。要跳过整个块,可以将内部迭代器(ii)包装在一个元组中,例如for c in tuple(ii),或者设置一个标志并耗尽迭代器。


为了避免所有转换为清单import itertools和:

1
2
>>> for k, g in itertools.groupby(xrange(35), lambda x: x/10):
...     list(g)

生产:

1
2
3
4
5
6
...
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
3 [30, 31, 32, 33, 34]
>>>

我检查了groupby,它没有转换成列表或使用len,所以我(认为)这将延迟每个值的分辨率,直到实际使用为止。可悲的是,(此时)所有可用的答案似乎都没有提供这种变化。

显然,如果您需要依次处理每个项,那么在g上嵌套一个for循环:

1
2
3
4
for k,g in itertools.groupby(xrange(35), lambda x: x/10):
    for i in g:
       # do what you need to do with individual items
    # now do what you need to do with the whole group

我对此特别感兴趣的是需要使用一个生成器,以将高达1000个批次的更改提交给Gmail API:

1
2
3
4
5
6
7
8
    messages = a_generator_which_would_not_be_smart_as_a_list
    for idx, batch in groupby(messages, lambda x: x/1000):
        batch_request = BatchHttpRequest()
        for message in batch:
            batch_request.add(self.service.users().messages().modify(userId='me', id=message['id'], body=msg_labels))
        http = httplib2.Http()
        self.credentials.authorize(http)
        batch_request.execute(http=http)


您可以使用Funcy库中的分区或块函数:

1
2
3
4
from funcy import partition

for a, b, c, d in partition(4, ints):
    foo += a * b * c * d

这些函数还有迭代器版本ipartitionichunks,在这种情况下效率更高。

您还可以查看它们的实现。


一个线性,即席的解决方案,在一个列表中迭代一块大小为4x。-

1
2
for a, b, c, d in zip(x[0::4], x[1::4], x[2::4], x[3::4]):
    ... do something with a, b, c and d ...

起初,我设计它将字符串拆分成子字符串来解析包含十六进制的字符串。今天我把它变成了一个复杂的,但仍然很简单的发电机。

1
2
3
4
5
6
7
8
def chunker(iterable, size, reductor, condition):
    it = iter(iterable)
    def chunk_generator():
        return (next(it) for _ in range(size))
    chunk = reductor(chunk_generator())
    while condition(chunk):
        yield chunk
        chunk = reductor(chunk_generator())

争论:明显的

  • iterable是包含/生成/迭代输入数据的任何iterable/iterator/generator,
  • 当然,size是你想要得到的块的大小,

更有趣

  • reductor是可调用的,它接收生成器对块内容的迭代。我希望它返回序列或字符串,但我不要求这样做。

    你可以把这个论点作为例子,比如listtuplesetfrozenset,或者其他更漂亮的东西。我将传递这个函数,返回字符串(前提是iterable包含/生成/迭代字符串):

    1
    2
    def concatenate(iterable):
        return ''.join(iterable)

    注意,reductor通过引发异常可能导致关闭发电机。

  • condition是可调用的,它接收reductor返回的任何内容。它决定批准并放弃它(通过将任何评估结果返回给True)或者拒绝并完成发电机的工作(返回任何其他内容或引发异常)。

    iterable中的元素数不可被size除尽时,it耗尽时,reductor将接收产生比size少元素的发电机。让我们称这些元素为持久元素。

    我邀请了两个函数作为参数传递:

    • lambda x:x—将生成最后的元素。

    • lambda x: len(x)==—最后的元素将被拒绝。用等于size的数字替换


似乎没有一个很好的方法来做到这一点。这是一个有很多方法的页面,包括:

1
2
3
4
5
6
def split_seq(seq, size):
    newseq = []
    splitsize = 1.0/size*len(seq)
    for i in range(size):
        newseq.append(seq[int(round(i*splitsize)):int(round((i+1)*splitsize))])
    return newseq

如果列表的大小相同,可以将它们与zip()组合成4个元组的列表。例如:

1
2
3
4
5
6
7
8
9
# Four lists of four elements each.

l1 = range(0, 4)
l2 = range(4, 8)
l3 = range(8, 12)
l4 = range(12, 16)

for i1, i2, i3, i4 in zip(l1, l2, l3, l4):
    ...

下面是zip()函数产生的结果:

1
2
3
4
5
6
7
8
9
10
>>> print l1
[0, 1, 2, 3]
>>> print l2
[4, 5, 6, 7]
>>> print l3
[8, 9, 10, 11]
>>> print l4
[12, 13, 14, 15]
>>> print zip(l1, l2, l3, l4)
[(0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15)]

如果列表很大,并且您不想将它们组合成一个更大的列表,那么使用生成迭代器的itertools.izip(),而不是列表。

1
2
3
4
from itertools import izip

for i1, i2, i3, i4 in izip(l1, l2, l3, l4):
    ...

这里有相当多的Python(你也可以把split_groups功能的身体连起来)

1
2
3
4
5
6
import itertools
def split_groups(iter_in, group_size):
    return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))

for x, y, z, w in split_groups(range(16), 4):
    foo += x * y + z * w

此答案拆分字符串列表,例如,为了实现PEP8行长度一致性:

1
2
3
4
5
6
7
8
9
10
11
12
def split(what, target_length=79):
    '''splits list of strings into sublists, each
    having string length at most 79'''

    out = [[]]
    while what:
        if len("', '".join(out[-1])) + len(what[0]) < target_length:
            out[-1].append(what.pop(0))
        else:
            if not out[-1]: # string longer than target_length
                out[-1] = [what.pop(0)]
            out.append([])
    return out

用作

1
2
>>> split(['deferred_income', 'long_term_incentive', 'restricted_stock_deferred', 'shared_receipt_with_poi', 'loan_advances', 'from_messages', 'other', 'director_fees', 'bonus', 'total_stock_value', 'from_poi_to_this_person', 'from_this_person_to_poi', 'restricted_stock', 'salary', 'total_payments', 'exercised_stock_options'], 75)
[['deferred_income', 'long_term_incentive', 'restricted_stock_deferred'], ['shared_receipt_with_poi', 'loan_advances', 'from_messages', 'other'], ['director_fees', 'bonus', 'total_stock_value', 'from_poi_to_this_person'], ['from_this_person_to_poi', 'restricted_stock', 'salary', 'total_payments'], ['exercised_stock_options']]

除非我遗漏了一些东西,否则下面关于生成器表达式的简单解决方案没有被提到。它假定块的大小和数量都是已知的(通常是这样),并且不需要填充:

1
2
3
4
5
6
def chunks(it, n, m):
   """Make an iterator over m first chunks of size n.
   """

    it = iter(it)
    # Chunks are presented as tuples.
    return (tuple(next(it) for _ in range(n)) for _ in range(m))

我希望通过将迭代器从列表中转出,我不只是复制列表的一部分。生成器可以被切片,并且它们仍然是一个生成器,而列表将被切片为1000个条目的大块,这会降低效率。

1
2
3
4
5
6
7
8
9
10
11
def iter_group(iterable, batch_size:int):
    length = len(iterable)
    start = batch_size*-1
    end = 0
    while(end < length):
        start += batch_size
        end += batch_size
        if type(iterable) == list:
            yield (iterable[i] for i in range(start,min(length-1,end)))
        else:
            yield iterable[start:end]

用途:

1
2
3
4
5
items = list(range(1,1251))

for item_group in iter_group(items, 1000):
    for item in item_group:
        print(item)

在不创建任何临时列表的情况下,很容易使itertools.groupby为您工作,以获得iterables的iterables:

1
groupby(iterable, (lambda x,y: (lambda z: x.next()/y))(count(),100))

不要被嵌套的lambda延迟,外部lambda只运行一次,将count()生成器和常量100放入内部lambda的范围。

我使用它向MySQL发送大量的行。

1
2
for k,v in groupby(bigdata, (lambda x,y: (lambda z: x.next()/y))(count(),100))):
    cursor.executemany(sql, v)