Итерировать по последовательности питона в кратных 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 ]

46 голосов
/ 17 апреля 2009

Функция генератора будет аккуратной:

def batch_gen(data, batch_size):
    for i in range(0, len(data), batch_size):
            yield data[i:i+batch_size]

Пример использования:

a = "abcdef"
for i in batch_gen(a, 2): print i

печать:

ab
cd
ef
13 голосов
/ 17 апреля 2009

У меня есть альтернативный подход, который работает для итераций, которые не имеют известной длины.

   
def groupsgen(seq, size):
    it = iter(seq)
    while True:
        values = ()        
        for n in xrange(size):
            values += (it.next(),)        
        yield values    

Он работает путем перебора последовательности (или другого итератора) в группах размера, собирая значения в кортеже. В конце каждой группы получается кортеж.

Когда у итератора заканчиваются значения, он генерирует исключение StopIteration, которое затем распространяется вверх, указывая, что groupsgen не имеет значений.

Предполагается, что значения входят в наборы размеров (наборы 2, 3 и т. Д.). Если нет, любые оставшиеся значения просто отбрасываются.

10 голосов
/ 17 апреля 2009

Не забудьте о функции zip ():

a = 'abcdef'
for x,y in zip(a[::2], a[1::2]):
  print '%s%s' % (x,y)
9 голосов
/ 17 апреля 2009

Я уверен, что кто-то собирается придумать еще немного "Pythonic", но как насчет:

for y in range(0, len(x), 2):
    print "%s%s" % (x[y], x[y+1])

Обратите внимание, что это будет работать, только если вы знаете, что len(x) % 2 == 0;

6 голосов
/ 24 апреля 2009

И всегда есть документация .

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    try:
        b.next()
    except StopIteration:
        pass
    return izip(a, b)

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)

Примечание: они производят кортежи вместо подстрок, когда в качестве входных данных задается последовательность строк.

6 голосов
/ 17 апреля 2009

, но более общий путь (вдохновленный этим ответом ):

for i in zip(*(seq[i::size] for i in range(size))):
    print(i)                            # tuple of individual values
4 голосов
/ 23 апреля 2009
>>> a = "abcdef"
>>> size = 2
>>> [a[x:x+size] for x in range(0, len(a), size)]
['ab', 'cd', 'ef']

.. или, не в качестве списка понимания:

a = "abcdef"
size = 2
output = []
for x in range(0, len(a), size):
    output.append(a[x:x+size])

Или, как генератор, который будет лучше, если его использовать несколько раз (для одноразового использования понимание списка, вероятно, «лучшее»):

def chunker(thelist, segsize):
    for x in range(0, len(thelist), segsize):
            yield thelist[x:x+segsize]

.. и его использование:

>>> for seg in chunker(a, 2):
...     print seg
... 
ab
cd
ef
3 голосов
/ 17 апреля 2009

вы можете создать следующий генератор

def chunks(seq, size):
    a = range(0, len(seq), size)
    b = range(size, len(seq) + 1, size)
    for i, j in zip(a, b):
        yield seq[i:j]

и используйте его так:

for i in chunks('abcdef', 2):
    print(i)
2 голосов
/ 06 декабря 2015

Из документов more_itertools : more_itertools.chunked()

more_itertools.chunked(iterable, n)

Разбить итерируемое на списки заданной длины:

>>> list(chunked([1, 2, 3, 4, 5, 6, 7], 3))
[[1, 2, 3], [4, 5, 6], [7]]

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

1 голос
/ 03 июня 2014

Документ itertools имеет рецепт для этого:

from itertools import izip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

Использование:

>>> l = [1,2,3,4,5,6,7,8,9]
>>> [z for z in grouper(l, 3)]
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...