Как сохранить itertools.chain и использовать его более одного раза?

Я хотел бы использовать itertools.chain для эффективного объединения списков (запоминания), но мне нужно иметь возможность читать (или map и т. д.) результат несколько раз. Этот пример иллюстрирует проблему:

import itertools
a = itertools.chain([1, 2], [3, 4])
print list(a) # => [1, 2, 3, 4]
print list(a) # => []

Каков наилучший способ избежать этой проблемы?


person jtbandes    schedule 31.10.2012    source источник


Ответы (2)


Как и во всех генераторах, вам нужно будет преобразовать его в список и вместо этого сохранить этот результат:

a = list(a)

Это фундаментальный принцип генераторов, ожидается, что они создадут свою последовательность только один раз.

Кроме того, вы не можете просто хранить генератор для целей запоминания, так как базовые списки могут измениться. Почти во всех случаях использования мемоизации вместо этого следует хранить список; генератор обычно является только средством эффективного преобразования или фильтрации базовых последовательностей и не представляет данные, которые вы хотите запомнить сами. Это как если бы вы сохраняли функцию, а не ее вывод. В вашем конкретном случае, если все, что вы делаете, это использование chain() для объединения существующих списков, вместо этого сохраняйте эти списки напрямую.

Обратите внимание, что это позволяет генераторам создавать бесконечные последовательности, поэтому будьте осторожны при преобразовании в список.

person Martijn Pieters    schedule 31.10.2012
comment
Спасибо за информацию. Похоже, я должен просто объединить списки. Есть ли более эффективный способ сделать это, чем list1 + list2 + ...? - person jtbandes; 31.10.2012
comment
@jtbandes: Вы можете использовать timeit для сравнения list(chain()) с list1 + .., но я подозреваю, что последний будет наиболее эффективным. - person Martijn Pieters; 31.10.2012

Попробуйте itertools.tee:

import itertools
a = itertools.chain([1, 2], [3, 4])
a, b = itertools.tee(a)
print list(b) # => [1, 2, 3, 4]
a, b = itertools.tee(a)
print list(b) # => [1, 2, 3, 4]
person georg    schedule 31.10.2012
comment
Это не меняет того факта, что запоминание генератора на самом деле бесполезно. - person Martijn Pieters; 31.10.2012
comment
Из документов: В общем, если один итератор использует большую часть или все данные до запуска другого итератора, быстрее использовать list() вместо tee(). - person Paolo Moretti; 31.10.2012
comment
@PaoloMoretti: обратите внимание, что автор не спрашивал, что быстрее или лучше, их вопрос заключается в том, как повторно использовать генератор, и itertools.tee предоставляет именно это. - person georg; 31.10.2012
comment
@ thg435 Мой комментарий - это скорее примечание для тех, кто может столкнуться с вопросом. - person Paolo Moretti; 31.10.2012