Подтвердить что ты не робот

Генератор Python, который группирует другую итерабельность в группы из N

Я ищу функцию, которая принимает итеративный i и размер n и дает кортежи длины n, которые являются последовательными значениями из i:

x = [1,2,3,4,5,6,7,8,9,0]
[z for z in TheFunc(x,3)]

дает

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

Существует ли такая функция в стандартной библиотеке?

Если он существует как часть стандартной библиотеки, я не могу найти его, и у меня закончились термины для поиска. Я мог бы написать свой собственный, но я бы предпочел не делать этого.

4b9b3361

Ответ 2

Если вы хотите сгруппировать итератор в кусках n без заполнения конечной группы с заполнением, используйте iter(lambda: list(IT.islice(iterable, n)), []):

import itertools as IT

def grouper(n, iterable):
    """
    >>> list(grouper(3, 'ABCDEFG'))
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
    """
    iterable = iter(iterable)
    return iter(lambda: list(IT.islice(iterable, n)), [])

seq = [1,2,3,4,5,6,7]
print(list(grouper(3, seq)))

дает

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

Есть объяснение того, как он работает во второй половине этого ответа.


Если вы хотите сгруппировать итератор в кусках n и pad конечной группы с заполняющим значением, используйте рецепт оргтера zip_longest(*[iterator]*n):

Например, в Python2:

>>> list(IT.izip_longest(*[iter(seq)]*3, fillvalue='x'))
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')]

В Python3 то, что было izip_longest, теперь переименовано zip_longest:

>>> list(IT.zip_longest(*[iter(seq)]*3, fillvalue='x'))
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')]

Если вы хотите сгруппировать последовательность в кусках n, вы можете использовать рецепт chunks:

def chunks(seq, n):
    # https://stackoverflow.com/a/312464/190597 (Ned Batchelder)
    """ Yield successive n-sized chunks from seq."""
    for i in xrange(0, len(seq), n):
        yield seq[i:i + n]

Обратите внимание, что, в отличие от итераторов вообще, последовательности по определению имеют длину (т.е. __len__).

Ответ 3

Как насчет этого? Однако он не имеет значения заполнения.

>>> def partition(itr, n):
...     i = iter(itr)
...     res = None
...     while True:
...             res = list(itertools.islice(i, 0, n))
...             if res == []:
...                     break
...             yield res
...
>>> list(partition([1, 2, 3, 4, 5, 6, 7, 8, 9], 3))
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>>

Он использует копию оригинального итеративного, который он исчерпывает для каждого последующего сращивания. Единственный другой способ, которым мой усталый мозг мог придумать, - создать конечные точки сращивания с диапазоном.

Возможно, мне нужно изменить list() на tuple(), чтобы он соответствовал вашему результату.

Ответ 4

Это очень распространенный запрос в Python. Достаточно распространен, что он превратил его в унифицированный пакет утилиты boltons. Во-первых, здесь есть обширные документы. Кроме того, модуль разработан и протестирован только для использования стандартной библиотеки (совместимы с Python 2 и 3), что означает просто загрузите файл непосредственно в свой проект.

# if you downloaded/embedded, try:
# from iterutils import chunked

# with `pip install boltons` use:

from boltons.iterutils import chunked 

print(chunked(range(10), 3))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Здесь есть итератор/генератор для неопределенных/длинных последовательностей:

print(list(chunked_iter(range(10), 3, fill=None)))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, None, None]]

Как вы можете видеть, вы также можете заполнить последовательность со значением по вашему выбору. Наконец, как сопровождающий, я могу заверить вас, что, хотя код был загружен или протестирован тысячами разработчиков, если у вас возникнут какие-либо проблемы, вы получите самую быструю поддержку через boltons Страница проблем GitHub. Надеюсь, что это (и/или любые другие 150+ рецепты болтонов) помогли!

Ответ 5

Я использую функцию chunked из пакета more_itertools.

$ pip install more_itertools
$ python
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> [tuple(z) for z in more_itertools.more.chunked(x, 3)]
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (0,)]

Ответ 6

Это очень старый вопрос, но я думаю, что полезно упомянуть следующий подход для общего случая. Его главное достоинство заключается в том, что ему нужно только итерировать данные один раз, поэтому он будет работать с курсорами базы данных или другими последовательностями, которые можно использовать только один раз. Я также нахожу это более читабельным.

def chunks(n, iterator):
    out = []
    for elem in iterator:
        out.append(elem)
        if len(out) == n:
            yield out
            out = []
    if out:
        yield out

Ответ 7

    def grouper(iterable, n):
        while True:
            yield itertools.chain((next(iterable),), itertools.islice(iterable, n-1))

Ответ 8

Я знаю, что это было несколько раз ответило, но я добавляю свое решение, которое должно улучшиться как в общей применимости к последовательностям, так и итераторам, читаемости (без исключения из замкнутого цикла по исключению StopIteration) и производительности по сравнению с рецептом группы, Это наиболее похоже на последний ответ Свейна.

def chunkify(iterable, n):
    iterable = iter(iterable)
    n_rest = n - 1

    for item in iterable:
        rest = itertools.islice(iterable, n_rest)
        yield itertools.chain((item,), rest)

Ответ 9

Вот другое решение, которое не использует itertools и, хотя оно имеет еще пару строк, оно, по-видимому, превосходит данные ответы, когда куски намного короче, чем итерируемая длина. Однако для больших фрагментов другие ответы намного быстрее.

def batchiter(iterable, batch_size):
    """
    >>> list(batchiter('ABCDEFG', 3))
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
    """
    next_batch = []
    for element in iterable:
        next_batch.append(element)
        if len(next_batch) == batch_size:
            batch, next_batch = next_batch, []
            yield batch
    if next_batch:
        yield next_batch


In [19]: %timeit [b for b in batchiter(range(1000), 3)]
1000 loops, best of 3: 644 µs per loop

In [20]: %timeit [b for b in grouper(3, range(1000))]
1000 loops, best of 3: 897 µs per loop

In [21]: %timeit [b for b in partition(range(1000), 3)]
1000 loops, best of 3: 890 µs per loop

In [22]: %timeit [b for b in batchiter(range(1000), 333)]
1000 loops, best of 3: 540 µs per loop

In [23]: %timeit [b for b in grouper(333, range(1000))]
10000 loops, best of 3: 81.7 µs per loop

In [24]: %timeit [b for b in partition(range(1000), 333)]
10000 loops, best of 3: 80.1 µs per loop