Итерировать по последовательности питона в кратных n? - PullRequest
25 голосов
/ 17 апреля 2009

Как обработать элементы последовательности в пакетном режиме, идиоматически?

Например, с последовательностью «abcdef» и размером пакета 2 я хотел бы сделать что-то вроде следующего:

for x, y in "abcdef":
    print "%s%s\n" % (x, y)
ab
cd
ef

Конечно, это не работает, потому что он ожидает один элемент из списка, который сам содержит 2 элемента.

Каков хороший, короткий, чистый, питонский способ обработки следующих n элементов списка в пакете или подстрок длины n из строки большего размера (две похожие проблемы)?

Ответы [ 16 ]

1 голос
/ 09 сентября 2009

s = 'abcdefgh'
for e in (s[i:i+2] for i in range(0,len(s),2)):
  print(e)
0 голосов
/ 29 апреля 2019

Вот решение, которое дает серию итераторов, каждый из которых выполняет итерацию по n элементам.

def groupiter(thing, n):
    def countiter(nextthing, thingiter, n):
        yield nextthing
        for _ in range(n - 1):
            yield next(thingiter)
    thingiter = iter(thing)
    while True:
        try:
            nextthing = next(thingiter)
        except StopIteration:
            return None
        yield countiter(nextthing, thingiter, n)

Я использую его следующим образом:

table = list(range(250))
for group in groupiter(table, 16):
    print(', '.join('0x{:02X}'.format(x) for x in group))

Обратите внимание, что он может обрабатывать длину объекта, не кратную n .

0 голосов
/ 22 июня 2017

С учетом

from __future__ import print_function                      # python 2.x

seq = "abcdef"
n = 2

код

while seq:
    print("{}".format(seq[:n]), end="\n")
    seq = seq[n:]

выход

ab
cd
ef
0 голосов
/ 30 сентября 2015

За исключением двух ответов, я видел много преждевременной материализации пакетов и подписки (что не работает для всех итераторов). Поэтому я придумал эту альтернативу:

def iter_x_and_n(iterable, x, n):
    yield x
    try:
        for _ in range(n):
            yield next(iterable)
    except StopIteration:
        pass

def batched(iterable, n):
    if n<1: raise ValueError("Can not create batches of size %d, number must be strictly positive" % n)
    iterable = iter(iterable)
    try:
        for x in iterable:
            yield iter_x_and_n(iterable, x, n-1)
    except StopIteration:
        pass

Меня удивляет, что для этого не существует решения с одним или несколькими вкладышами (насколько мне известно). Ключевая проблема заключается в том, что как внешний генератор, так и внутренний генератор должны правильно обрабатывать StopItered. Внешний генератор должен давать что-то, только если остался хотя бы один элемент. Интуитивно понятный способ проверить это - выполнить следующее (...) и поймать StopIteration.

0 голосов
/ 23 апреля 2009

Как насчет itertools?

from itertools import islice, groupby

def chunks_islice(seq, size):
    while True:
        aux = list(islice(seq, 0, size))
        if not aux: break
        yield "".join(aux)

def chunks_groupby(seq, size):
    for k, chunk in groupby(enumerate(seq), lambda x: x[0] / size):
        yield "".join([i[1] for i in chunk])
0 голосов
/ 17 апреля 2009

Одно решение, хотя я призываю кого-то сделать лучше; -)

a = 'abcdef'
b = [[a[i-1], a[i]] for i in range(1, len(a), 2)]

for x, y in b:
  print "%s%s\n" % (x, y)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...