Какой самый «питонный» способ перебрать список по частям? - PullRequest
407 голосов
/ 12 января 2009

У меня есть скрипт Python, который принимает в качестве входных данных список целых чисел, которые мне нужно работать с четырьмя целыми числами одновременно. К сожалению, у меня нет контроля над входом, или я бы передал его в виде списка из четырех элементов. В настоящее время я повторяю это так:

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", что заставляет меня подозревать, что есть более питонический способ справиться с этой ситуацией. Список отбрасывается после итерации, поэтому его не нужно сохранять. Возможно, что-то вроде этого будет лучше?

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

Тем не менее, все еще не совсем "чувствую" себя хорошо. : - /

Смежный вопрос: Как разбить список на куски одинакового размера в Python?

Ответы [ 35 ]

0 голосов
/ 25 февраля 2016

Легко заставить itertools.groupby работать для вас, чтобы получить итерируемые итерируемые элементы без создания временных списков:

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

Не откладывайте вложенные лямбды, внешняя лямбда запускается всего один раз, чтобы поместить count() генератор и постоянную 100 в область действия внутренней лямбды.

Я использую это для отправки кусков строк в MySQL.

for k,v in groupby(bigdata, (lambda x,y: (lambda z: x.next()/y))(count(),100))):
    cursor.executemany(sql, v)
0 голосов
/ 26 ноября 2014

Сначала я разработал его для разделения строк на подстроки для анализа строки, содержащей шестнадцатеричное число.
Сегодня я превратил его в сложный, но все же простой генератор.

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 - это вызываемый объект, который получает генератор, перебирающий содержимое чанка.
    Я ожидаю, что он вернет последовательность или строку, но я этого не требую.

    Вы можете передать в качестве этого аргумента, например, list, tuple, set, frozenset,
    или что-нибудь более причудливое. Я бы передал эту функцию, возвращая строку
    (при условии, что iterable содержит / генерирует / перебирает строки):

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

    Обратите внимание, что reductor может вызвать закрытие генератора, вызвав исключение.

  • condition - это вызываемый объект, который получает все, что вернул reductor.
    Он решает одобрить и выдать его (возвращая все, что оценивается как True),
    или отказаться от него и завершить работу генератора (вернув что-нибудь другое или подняв исключение).

    Когда число элементов в iterable не делится на size, когда it истощается, reductor получит генератор, генерирующий меньше элементов, чем size.
    Давайте назовем эти элементы длится элементы .

    Я предложил передать в качестве аргумента две функции:

    • lambda x:x - длится элементы будет получен.

    • lambda x: len(x)==<size> - последние элементы будут отклонены.
      заменить <size>, используя число, равное size

0 голосов
/ 01 сентября 2014

Однострочное, временное решение для итерации по списку x кусками размера 4 -

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 ...
0 голосов
/ 13 мая 2019

Почему бы не использовать списочное понимание

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]]
0 голосов
/ 12 января 2009

Кажется, что нет красивого способа сделать это. Здесь - это страница, которая имеет несколько методов, в том числе:

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