Преимущества производительности для итераторов?

Что (если есть) преимущества производительности предлагаются с помощью итераторов. Кажется, что "правильный путь" позволяет решить многие проблемы, но создает ли он более быстрый/более сознательный код? Я думаю конкретно на Python, но не ограничиваю только ответы на это.

Ответ 1

На самом деле очень хорошая почта в списке рассылки python об этом: Iterators vs Lists. Он немного устарел (с 2003 года), но, насколько я знаю, он все еще действителен.

Здесь резюме:

Для небольших наборов данных итераторы и подходы на основе списков аналогичны представление. Для больших наборов данных итераторы сохраняют время и пространство.

Что я хотел бы извлечь из этого: iterators должны быть предпочтительнее по возможности загружать данные в список. Но если у вас нет большого набора данных, не мешайте вашему коду делать что-то, что должно вписываться в список, чтобы работать с итератором.

Ответ 2

Для Python генераторы будут быстрее и имеют лучшую эффективность памяти. Просто подумайте о примере range(1000) vs xrange(1000) (это было изменено в версии 3.0, теперь это диапазон). С Range вы предварительно создаете свой список, но XRange просто имеет объект-генератор и при необходимости возвращает следующий элемент.

Разница в производительности невелика по мелочам, но как только вы начнете ее провожать, вы получаете большие и большие наборы информации, вы заметите ее довольно быстро. Кроме того, вы не просто должны генерировать, а затем переходить на другой уровень, вы будете потреблять дополнительную память для своего предварительно созданного элемента, где, как и при выражении генератора, создается только 1 элемент.

Ответ 3

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

Мой любимый пример использования итератора можно найти в С++ стандартная библиотека шаблонов. Ему удается продемонстрировать силу и красоту абстракции, чисто разделяя контейнер и алгоритм, не жертвуя производительностью. Понимание этого проекта оказало глубокое влияние на то, как я думаю о коде.

Ответ 4

Для резервного копирования @Christian Witts ответ:

range против xrange производительность

python25 -mtimeit "for i in xrange(1000): pass"
10000 loops, best of 3: 56.3 usec per loop

python25 -mtimeit "for i in range(1000): pass"
10000 loops, best of 3: 80.9 usec per loop

python26 -mtimeit "for i in xrange(1000): pass"
10000 loops, best of 3: 48.8 usec per loop

python26 -mtimeit "for i in range(1000): pass"
10000 loops, best of 3: 68.6 usec per loop

btw, ни range(), ни xrange() не являются итераторами:

>>> hasattr(range(1), 'next')
False
>>> hasattr(xrange(1), 'next')
False
>>> iter(xrange(1))
<rangeiterator object at 0x0097A500>
>>> iter(range(1))
<listiterator object at 0x00A7BFD0>
>>> iter([])
<listiterator object at 0x00A7BE30>
>>> iter(i for i in (1,))
<generator object at 0x00A7F940>
>>> (i for i in (1,))
<generator object at 0x00A7FDC8>

Ответ 5

Итераторы - это просто классы, которые реализуют конкретный интерфейс, а именно интерфейс для перехода к следующему. В Python списки, кортежи, дикты, строки и файлы реализуют весь этот интерфейс. Если они реализованы плохо, это может привести к низкой производительности, но интерфейс не имеет ничего особенного, что подразумевает хорошую или плохую производительность.

Ответ 6

Мой вывод из многих приведенных выше ответов - "Использовать список для кода. Если необходимо, повторите фактор с помощью итераторов". Разница не очевидна, если у вас нет большого набора данных.

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

Ответ 7

Итератор - это просто объект, который предоставляет методы, позволяющие проходить через коллекцию. Вы можете пересечь все элементы массива или все узлы дерева с одним и тем же интерфейсом. Деревья и массивы представляют собой очень разные структуры данных и требуют разных методов для перемещения. Но с помощью итератора вы можете прокручивать все элементы таким же образом.

Для одного типа коллекции также могут быть разные способы перемещения по нему, а в одной коллекции могут быть несколько итераторов. У вас может быть итератор глубины или первый итератор ширины, проходящий по древовидной структуре и возвращающий узлы в разных порядках. Итераторы не предназначены для производительности... но обычно для обеспечения согласованного интерфейса для перемещения структур.

Ответ 8

Есть один ответ, который, я думаю, немного путает концепцию генератора и итератора. Поэтому я решил попробовать ответить на этот вопрос с помощью примера метафоры.

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

  • Итерируемый объект: Каждый хлеб хранится в одной коробке (память), я взвешиваю первый (или 0-й) хлеб, откладываю его вес и помещаю хлеб обратно в коробку, затем переходим к следующему, взвешиваем его и возвращаем обратно, и т.д. и т.д. В конце концов, я получил общий вес, а 10 (100 или миллион) хлебов все еще присутствуют в их коробках.

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

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

В любом случае, подведем итог:

  • Итерируемому объекту нужна некоторая память для хранения данных для начала. В конце все еще есть данные.

  • Генератору не нужна память для хранения данных для начала, он генерирует данные в пути.

  • Итератор - это канал между алгоритмом и его данными. Эти данные могут быть уже там и сохранены в памяти или могут быть сгенерированы на ходу генератором. В первом случае эта память будет постепенно сокращаться по мере продолжения итератора. Поэтому я очень согласен с вышеприведенным ответом, что итератор хорош из-за его абстракции, которая позволяет изолировать алгоритм и данные.

python не работает точно так. Надеюсь, что это поможет прояснить немного.

Ответ 9

Немного не по теме, но добавляет больше веса к использованию списков за итераторами в целом: с итераторами проще иметь побочные эффекты, подумайте об этом:

def foo(arg: Iterable[str]):
  print(list(arg)) # side effect: arg is exhausted at this point
  ...

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