У меня есть сценарий 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?
Ответы:
Изменено в разделе рецептов документации iterotools Python :
Пример
В псевдокоде оставить пример кратким.
Примечание: на Python 2 используйте
izip_longest
вместоzip_longest
.источник
izip_longest
будет передаваться 256К аргументов.None
заполнения последнего куска?Просто. Легко. Быстрый. Работает с любой последовательностью:
источник
itertools
модулем.chunker
возвращаетgenerator
. Заменить возврат на:return [...]
чтобы получить список.yield
:for pos in xrange(0, len(seq), size): yield seq[pos:pos + size]
. Я не уверен, что внутренне это будет обрабатываться по-другому в любом соответствующем аспекте, но это может быть даже немного яснее.__getitem__
метод.Я фанат
источник
chunk
будет иметь 1, 2 или 3 элемента для последней партии элементов. Посмотрите этот вопрос о том, почему индексы срезов могут выходить за пределы .Другой путь:
источник
size
, что иногда желательно.len
вызова и поэтому не работают на других генераторах.источник
izip_longest
заменяется наzip_longest
Идеальное решение этой проблемы работает с итераторами (а не только с последовательностями). Это также должно быть быстро.
Это решение, предоставляемое документацией для itertools:
Использование ipython
%timeit
на моем , я получаю 47,5 нас за цикл.Тем не менее, это действительно не работает для меня, так как результаты дополняются до групп равного размера. Решение без дополнения немного сложнее. Наиболее наивное решение может быть:
Простой, но довольно медленный: 693 нас за цикл
Лучшее решение, которое я мог придумать, использует
islice
для внутреннего цикла:С тем же набором данных я получаю 305 нас за цикл.
Не имея возможности получить чистое решение быстрее, чем это, я предлагаю следующее решение с одной важной оговоркой: если в ваших входных данных есть экземпляры
filldata
, вы можете получить неправильный ответ.Мне действительно не нравится этот ответ, но он значительно быстрее. 124 доллара за петлю
источник
itertools
импорт,map
должны быть PY3map
илиimap
):def grouper(n, it): return takewhile(bool, map(tuple, starmap(islice, repeat((iter(it), n)))))
. Ваша последняя функция может быть сделана менее хрупкой с помощью часового: избавьтесь отfillvalue
аргумента; добавьте первую строкуfillvalue = object()
, затем изменитеif
чек наif i[-1] is fillvalue:
и строку, которой он управляетyield tuple(v for v in i if v is not fillvalue)
. Гарантии, что никакое значение в неiterable
может быть принято за значение наполнителя.islice
объектов (# 3 выигрывает, еслиn
относительно велико, например, количество групп мало, но это оптимизирует для нечастого случая), но я не ожидал, что это будет так крайность.izip_longest
на конечном наборе:yield i[:modulo]
. Кроме того , дляargs
переменной, кортеж его вместо списка:args = (iter(iterable),) * n
. Бреет еще несколько тактов. Наконец, если мы проигнорируем fillvalue и предположимNone
, что условное условие может статьif None in i
еще больше тактов.yield
), в то время как общий случай не затронут.Мне нужно решение, которое также будет работать с наборами и генераторами. Я не мог придумать ничего очень короткого и красивого, но это, по крайней мере, вполне читабельно.
Список:
Набор:
Генератор:
источник
Подобно другим предложениям, но не совсем идентично, мне нравится делать это таким образом, потому что это просто и легко читается:
Таким образом, вы не получите последний частичный кусок. Если вы хотите получить
(9, None, None, None)
последний кусок, просто используйтеizip_longest
изitertools
.источник
zip(*([it]*4))
Если вы не возражаете против использования внешнего пакета, вы можете использовать
iteration_utilities.grouper
с 1 . Он поддерживает все итерации (не только последовательности):iteration_utilties
который печатает:
Если длина не кратна размеру группы, он также поддерживает заполнение (неполная последняя группа) или усечение (отбрасывание неполной последней группы) последней:
Ориентиры
Я также решил сравнить время выполнения некоторых из упомянутых подходов. Это график журнала, объединяющий группы по 10 элементов на основе списка разного размера. Для качественных результатов: чем ниже, тем быстрее:
По крайней мере, в этом тесте результаты
iteration_utilities.grouper
работают лучше всего. Затем следует подход Craz .Тест был создан с 1 . Код, используемый для запуска этого теста:
simple_benchmark
1 Отказ от ответственности: я автор библиотек
iteration_utilities
иsimple_benchmark
.источник
Поскольку никто еще не упомянул об этом, вот
zip()
решение:Это работает только в том случае, если длина вашей последовательности всегда делится на размер фрагмента или вы не заботитесь о конечном фрагменте, если это не так.
Пример:
Или используя itertools.izip для возврата итератора вместо списка:
Заполнение можно исправить с помощью ответа @ ΤΖΩΤΖΙΟΥ :
источник
Использование map () вместо zip () решает проблему заполнения в ответе JF Sebastian:
Пример:
источник
itertools.izip_longest
(Py2) /itertools.zip_longest
(Py3); такое использованиеmap
не рекомендуется вдвойне и недоступно в Py3 (вы не можете передатьNone
как функцию отображения, и оно останавливается, когда самая короткая итерация исчерпана, а не самая длинная; она не дополняется).Другой подход заключается в использовании формы с двумя аргументами
iter
:Это может быть легко адаптировано для использования отступов (это похоже на ответ Маркуса Жардеро ):
Их можно даже комбинировать для дополнительного заполнения:
источник
Если список большой, самый эффективный способ сделать это - использовать генератор:
источник
iterable = range(100000000)
&chunksize
до 10000.Использование маленьких функций и вещей действительно не привлекает меня; Я предпочитаю просто использовать ломтики:
источник
len
. Вы можете сделать тест с помощьюitertools.repeat
илиitertools.cycle
.[...for...]
понимания списков для физического построения списка вместо использования(...for...)
выражения-генератора, которое просто заботится о следующем элементе и резервной памятиЧтобы избежать всех преобразований в список
import itertools
и:Производит:
Я проверил,
groupby
и он не конвертируется в список или не использует,len
поэтому (думаю) это задержит разрешение каждого значения, пока оно не будет фактически использовано. К сожалению, ни один из доступных ответов (в настоящее время), казалось, не предлагал этот вариант.Очевидно, что если вам нужно обрабатывать каждый элемент по очереди, вложите цикл a в g:
Мой особый интерес в этом заключался в необходимости использования генератора для отправки изменений в пакетах до 1000 в API gmail:
источник
groupby(messages, lambda x: x/3)
вы бы получили ошибку TypeError (для попытки разделить строку на int), а не 3-буквенные группировки. Теперь, если бы ты это сделал, уgroupby(enumerate(messages), lambda x: x[0]/3)
тебя могло бы быть что-то. Но вы не сказали этого в своем посте.С NumPy все просто:
вывод:
источник
источник
Если я не пропустил что-то, следующее простое решение с выражениями генератора не было упомянуто. Предполагается, что известны как размер, так и количество фрагментов (что часто имеет место) и что заполнение не требуется:
источник
В вашем втором методе я бы перейти к следующей группе из 4, выполнив это:
Тем не менее, я не делал никаких измерений производительности, поэтому я не знаю, какой из них может быть более эффективным.
Сказав это, я обычно выбираю первый метод. Это не красиво, но это часто является следствием взаимодействия с внешним миром.
источник
Еще один ответ, преимуществами которого являются:
1) Легко понять
2) Работает с любыми повторяемыми, а не только с последовательностями (некоторые из приведенных выше ответов захлебнутся файловыми дескрипторами)
3) Не загружает блок в память сразу
4) Не создает длинный список ссылок на тот же итератор в памяти
5) Нет заполнения значений заполнения в конце списка
При этом я не рассчитал время, поэтому оно может быть медленнее, чем некоторые из более умных методов, и некоторые преимущества могут быть неактуальными, учитывая вариант использования.
Обновление:
пара недостатков, связанных с тем, что внутренний и внешний циклы извлекают значения из одного и того же итератора:
1) продолжение не работает должным образом во внешнем цикле - оно просто переходит к следующему элементу, а не пропускает фрагмент , Тем не менее, это не кажется проблемой, так как во внешнем цикле проверять нечего.
2) разрыв не работает так, как ожидалось во внутреннем цикле - элемент управления снова окажется во внутреннем цикле со следующим элементом в итераторе. Чтобы пропустить целые куски, либо оберните внутренний итератор (ii выше) в кортеж, например
for c in tuple(ii)
, либо установите флаг и исчерпайте итератор.источник
источник
Вы можете использовать функцию разбиения или чанков из библиотеки funcy :
Эти функции также имеют версии итераторов
ipartition
иichunks
, что будет более эффективно в этом случае.Вы также можете посмотреть на их реализацию .
источник
О решении, представленном
J.F. Sebastian
здесь :Это умно, но имеет один недостаток - всегда возвращать кортеж. Как получить строку вместо?
Конечно можно написать
''.join(chunker(...))
, но временный кортеж построен в любом случае.Вы можете избавиться от временного кортежа, написав собственный
zip
, например:затем
Пример использования:
источник
zip
вместо использования существующего, кажется, не лучшая идея.Мне нравится этот подход. Он прост и не волшебен, поддерживает все повторяемые типы и не требует импорта.
источник
Я никогда не хочу, чтобы мои куски были мягкими, поэтому это требование крайне важно. Я считаю, что умение работать на любом итерируемом также является требованием. Учитывая это, я решил расширить принятый ответ, https://stackoverflow.com/a/434411/1074659 .
При таком подходе производительность незначительно падает, если заполнение не требуется из-за необходимости сравнивать и фильтровать дополненные значения. Однако для больших размеров куска эта утилита очень эффективна.
источник
Вот чанкер без импорта, который поддерживает генераторы:
Пример использования:
источник
С Python 3.8 вы можете использовать оператор моржа и
itertools.islice
.источник
Там, кажется, не очень хороший способ сделать это. Вот страница, которая имеет несколько методов, в том числе:
источник
Если списки имеют одинаковый размер, вы можете объединить их в списки из 4-х кортежей
zip()
. Например:Вот что
zip()
выдает функция:Если списки велики, и вы не хотите объединять их в больший список, используйте
itertools.izip()
, который создает итератор, а не список.источник
Однострочное, временное решение для перебора списка
x
по размерам4
-источник