簡體   English   中英

如何以塊的形式迭代列表

[英]How to iterate over a list in chunks

我有一個 Python 腳本,它將整數列表作為輸入,我需要一次處理四個整數。 不幸的是,我無法控制輸入,否則我會將其作為四元素元組列表傳入。 目前,我正在以這種方式對其進行迭代:

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

不過,它看起來很像“C-think”,這讓我懷疑有一種更 Pythonic 的方式來處理這種情況。 該列表在迭代后被丟棄,因此不需要保留。 也許這樣的事情會更好?

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

不過,仍然不太“感覺”正確。 :-/

相關問題: 如何在 Python 中將列表拆分為大小均勻的塊?

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)

適用於任何序列:

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文檔的食譜部分修改:

from itertools import zip_longest

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

例子

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

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

chunk_size = 4
for i in range(0, len(ints), chunk_size):
    chunk = ints[i:i+chunk_size]
    # process chunk of size <= chunk_size
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)

另一種方式:

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

如果您不介意使用外部包,您可以使用來自iteration_utilities.grouper 1iteration_utilties 它支持所有可迭代對象(不僅僅是序列):

from iteration_utilities import grouper
seq = list(range(20))
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, 17, 18, 19)

如果長度不是 groupsize 的倍數,它還支持填充(不完整的最后一組)或截斷(丟棄不完整的最后一組)最后一個:

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)

基准

我還決定比較一些上述方法的運行時間。 這是一個對數圖,根據不同大小的列表分組為“10”個元素組。 對於定性結果: 越低意味着越快:

在此處輸入圖像描述

至少在這個基准測試中, iteration_utilities.grouper表現最好。 緊隨其后的是克拉茲的逼近。

基准是用simple_benchmark 1創建的。 用於運行此基准測試的代碼是:

import iteration_utilities
import itertools
from itertools import zip_longest

def consume_all(it):
    return iteration_utilities.consume(it, None)

import simple_benchmark
b = simple_benchmark.BenchmarkBuilder()

@b.add_function()
def grouper(l, n):
    return consume_all(iteration_utilities.grouper(l, n))

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

@b.add_function()
def Craz(iterable, n, fillvalue=None):
    return consume_all(Craz_inner(iterable, n, fillvalue))

def nosklo_inner(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))

@b.add_function()
def nosklo(seq, size):
    return consume_all(nosklo_inner(seq, size))

def SLott_inner(ints, chunk_size):
    for i in range(0, len(ints), chunk_size):
        yield ints[i:i+chunk_size]

@b.add_function()
def SLott(ints, chunk_size):
    return consume_all(SLott_inner(ints, chunk_size))

def MarkusJarderot1_inner(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

@b.add_function()
def MarkusJarderot1(iterable,size):
    return consume_all(MarkusJarderot1_inner(iterable,size))

def MarkusJarderot2_inner(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))

@b.add_function()
def MarkusJarderot2(iterable,size):
    return consume_all(MarkusJarderot2_inner(iterable,size))

@b.add_arguments()
def argument_provider():
    for exp in range(2, 20):
        size = 2**exp
        yield size, simple_benchmark.MultiArgument([[0] * size, 10])

r = b.run()

1免責聲明:我是庫iteration_utilitiessimple_benchmark的作者。

我需要一個同樣適用於集合和生成器的解決方案。 我想不出任何非常簡短和漂亮的東西,但至少它是相當可讀的。

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

列表:

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

放:

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

發電機:

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

這個問題的理想解決方案適用於迭代器(不僅僅是序列)。 它也應該很快。

這是 itertools 文檔提供的解決方案:

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

在我的 mac book air 上使用 ipython 的%timeit ,每個循環我得到 47.5 us。

但是,這對我來說真的不起作用,因為結果被填充為均勻大小的組。 沒有填充的解決方案稍微復雜一些。 最天真的解決方案可能是:

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作為內部循環:

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實例,您可能會得到錯誤的答案。

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    # itertools.zip_longest on Python 3
    for x in itertools.izip_longest(*args, fillvalue=fillvalue):
        if x[-1] is fillvalue:
            yield tuple(v for v in x if v is not fillvalue)
        else:
            yield x

我真的不喜歡這個答案,但它明顯更快。 每個循環 124 us

more-itertools包有chunked方法,它可以做到這一點:

import more_itertools
for s in more_itertools.chunked(range(9), 4):
    print(s)

印刷

[0, 1, 2, 3]
[4, 5, 6, 7]
[8]

chunked返回列表中的項目。 如果您更喜歡迭代,請使用ichunked

在 Python 3.8 中,您可以使用 walrus 運算符和itertools.islice

from itertools import islice

list_ = [i for i in range(10, 100)]

def chunker(it, size):
    iterator = iter(it)
    while chunk := list(islice(iterator, size)):
        print(chunk)
In [2]: chunker(list_, 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, 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]

from itertools import izip_longest

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

與其他提案類似,但不完全相同,我喜歡這樣做,因為它簡單易讀:

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)作為最后一個塊,只需使用izip_longest from itertools

由於沒有人提到它,這里有一個zip()解決方案:

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

它僅在您的序列長度始終可以被塊大小整除或者您不關心尾隨塊(如果不是)時才有效。

例子:

>>> 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返回一個迭代器而不是一個列表:

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

填充可以使用@ΤZΩΤZΙΟΥ's answer修復:

>>> 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)

另一種方法是使用iter的兩個參數形式:

from itertools import islice

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

這可以很容易地適應使用填充(這類似於Markus Jarderot的回答):

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)

這些甚至可以組合用於可選填充:

_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)

使用 map() 而不是 zip() 修復了 JF Sebastian 回答中的填充問題:

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

例子:

>>> 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')]

使用小功能和東西真的不吸引我; 我更喜歡只使用切片:

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:
    ...

如果列表很大,執行此操作的最高性能方法是使用生成器:

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,)

以大小為4的塊迭代列表x的單線、臨時解決方案 -

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 ...

為避免所有轉換為列表,請import itertools並:

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

產生:

... 
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並且它沒有轉換為 list 或使用len所以我(認為)這將延遲每個值的解析,直到它被實際使用。 遺憾的是(此時)似乎沒有一個可用的答案提供這種變化。

顯然,如果您需要依次處理每個項目,請在 g 上嵌套一個 for 循環:

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:

    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)

使用 NumPy 很簡單:

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
5 6
7 8
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()

除非我錯過了什么,否則沒有提到以下帶有生成器表達式的簡單解決方案。 它假設塊的大小和數量都是已知的(通常是這種情況),並且不需要填充:

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))

另一個答案,其優點是:

1) 易於理解
2)適用於任何可迭代的,而不僅僅是序列(上面的一些答案會阻塞文件句柄)
3) 不會一次將塊加載到內存中
4) 不在內存中創建一個對同一個迭代器的引用的大塊列表
5) 列表末尾沒有填充值

話雖如此,我還沒有計時,所以它可能比一些更聰明的方法慢,並且考慮到用例,一些優點可能無關緊要。

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) ,或者設置一個標志並耗盡迭代器。

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

您可以使用funcy庫中的partitionchunks函數:

from funcy import partition

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

這些函數還有迭代器版本ipartitionichunks ,在這種情況下效率更高。

您還可以查看它們的實現

關於JF Sebastian在這里給出的解決方案:

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

它很聰明,但有一個缺點——總是返回元組。 如何獲取字符串?
當然你可以寫''.join(chunker(...)) ,但是臨時元組無論如何都會被構造。

您可以通過編寫自己的zip來擺脫臨時元組,如下所示:

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

然后

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

示例用法:

>>> 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'

這是一個沒有導入支持生成器的分塊器:

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()

使用示例:

>>> 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 人:

ints = ints[4:]

但是,我沒有進行任何性能測量,所以我不知道哪一個可能更有效。

話雖如此,我通常會選擇第一種方法。 它並不漂亮,但這通常是與外部世界交互的結果。

我喜歡這種方法。 它感覺簡單而不神奇,並且支持所有可迭代類型並且不需要導入。

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

這里非常pythonic(您也可以內聯split_groups函數的主體)

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

我從不希望我的大塊被填充,所以這個要求是必不可少的。 我發現處理任何迭代的能力也是必需的。 鑒於此,我決定擴展已接受的答案https://stackoverflow.com/a/434411/1074659

如果由於需要比較和過濾填充值而不需要填充,則此方法的性能會受到輕微影響。 但是,對於大塊大小,此實用程序非常高效。

#!/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

這是我在列表,迭代器和范圍上的工作......懶惰:

def chunker(it,size):
    rv = [] 
    for i,el in enumerate(it,1) :   
        rv.append(el)
        if i % size == 0 : 
            yield rv
            rv = []
    if rv : yield rv        

幾乎使它成為單線;(

In [95]: list(chunker(range(9),2) )                                                                                                                                          
Out[95]: [[0, 1], [2, 3], [4, 5], [6, 7], [8]]

In [96]: list(chunker([1,2,3,4,5],2) )                                                                                                                                       
Out[96]: [[1, 2], [3, 4], [5]]

In [97]: list(chunker(iter(range(9)),2) )                                                                                                                                    
Out[97]: [[0, 1], [2, 3], [4, 5], [6, 7], [8]]

In [98]: list(chunker(range(9),25) )                                                                                                                                         
Out[98]: [[0, 1, 2, 3, 4, 5, 6, 7, 8]]

In [99]: list(chunker(range(9),1) )                                                                                                                                          
Out[99]: [[0], [1], [2], [3], [4], [5], [6], [7], [8]]

In [101]: %timeit list(chunker(range(101),2) )                                                                                                                               
11.3 µs ± 68.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

起初,我將其設計為將字符串拆分為子字符串以解析包含十六進制的字符串。
今天我把它變成了復雜但仍然簡單的生成器。

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是包含/生成/迭代輸入數據的任何可迭代/迭代器/生成器,
  • size當然是你想要得到的塊的大小,

更有意思的

  • reductor是一個可調用對象,它接收生成器對塊內容的迭代。
    我希望它返回序列或字符串,但我不要求這樣做。

    您可以作為此參數傳遞,例如listtuplesetfrozenset
    或任何更花哨的東西。 我會通過這個函數,返回字符串
    (前提是iterable包含/生成/迭代字符串):

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

    請注意, reductor可以通過引發異常來導致關閉生成器。

  • condition是一個可調用的,它接收reductor返回的任何東西。
    它決定批准並放棄它(通過返回任何評估為True ),
    或拒絕它並完成生成器的工作(通過返回任何其他內容或引發異常)。

    iterable中的元素數量不能被size整除時,當it耗盡時, reductor將接收生成器生成的元素少於size
    我們稱這些元素為持續元素

    我邀請了兩個函數作為這個參數傳遞:

    • lambda x:x - 將產生lasts 元素

    • lambda x: len(x)==<size> - lasts 元素將被拒絕。
      使用等於size的數字替換<size>

很容易讓itertools.groupby為您工作以獲得可迭代的可迭代對象,而無需創建任何臨時列表:

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

不要被嵌套的 lambdas 推遲,外部 lambda 只運行一次以將count()生成器和常量100放入內部 lambda 的范圍內。

我用它來向 mysql 發送大塊的行。

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

這個答案拆分了一個字符串列表,f.ex。 實現 PEP8 行長度合規性:

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

用於

>>> 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']]

我希望通過將迭代器從列表中移出,我不僅僅是復制列表的一部分。 生成器可以被切片,它們仍然會自動成為一個生成器,而列表將被切片成 1000 個條目的大塊,效率較低。

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]

用法:

items = list(range(1,1251))

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

在我的特殊情況下,我需要填充項目以重復最后一個元素直到它達到大小,所以我改變了這個答案以滿足我的需要。

所需輸入 output 大小為 4 的示例:

Input = [1,2,3,4,5,6,7,8]
Output= [[1,2,3,4], [5,6,7,8]]

Input = [[1,2,3,4,5,6,7]]
Output= [[1,2,3,4], [5,6,7,7]]

Input = [1,2,3,4,5]
Output= [[1,2,3,4], [5,5,5,5]]
def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        res = res + (size - len(res)) * [res[-1]] 
        yield res

似乎沒有一個很好的方法來做到這一點。 是一個包含多種方法的頁面,包括:

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 元組列表。 例如:

# 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()函數產生的結果:

>>> 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() ,它會生成迭代器,而不是列表。

from itertools import izip

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

為什么不使用列表理解

l = [1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
n = 4
filler = 0
fills = len(l) % n
chunks = ((l + [filler] * fills)[x * n:x * n + n] for x in range(int((len(l) + n - 1)/n)))
print(chunks)

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 0]]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM