2015-05-08 1 views
4

В следующем примере показано различное поведение в зависимости от того, является ли самый правый генератор в понимании списка списком или итератором. В частности, при использовании итератора генерируется меньшее количество результатов. Я считаю, что это поведение очень удивительно.Элементы отсутствуют, когда итератор используется в понимании списка

Я новичок в Python, поэтому, я думаю, что мне не хватает чего-то очевидного, но я был бы благодарен за объяснение.

>>> import itertools 
>>> xs = range(0, 5) 
>>> ys = range(0, 3) 
>>> zs_l = [x for x in xs if not x in ys] 
>>> zs_l 
[3, 4] 

# Validate the contents of the iterator, and create it again 
>>> zs_i = itertools.ifilterfalse(lambda x: x in ys, xs) 
>>> list(zs_i) 
[3, 4] 
>>> list(zs_i) 
[] 
>>> zs_i = itertools.ifilterfalse(lambda x: x in ys, xs) 

>>> [(i,z) for i in [1,2] for z in zs_l] 
[(1, 3), (1, 4), (2, 3), (2, 4)] 
>>> [(i,z) for i in [1,2] for z in zs_i] 
[(1, 3), (1, 4)] 

ответ

2

Цитирование itertools.ifilterfalse документы,

сделать итератор, который фильтрует элементы ...

Цитирование документации питона на срок iterator,

Объект, представляющий поток данных. Повторные вызовы метода итератора next() возвращают последовательные элементы в потоке. Если больше нет данных, вместо этого возникает исключение StopIteration. На этом этапе объект итератора исчерпан, и любые дальнейшие вызовы его метода next() снова поднимут StopIteration. Итераторы должны иметь метод __iter__(), который возвращает сам объект итератора, поэтому каждый итератор также можно итерабельно и может использоваться в большинстве мест, где принимаются другие итерации. Одним из примечательных исключений является код, который пытается выполнить несколько итераций. Объект-контейнер (например, list) создает новый новый итератор каждый раз, когда вы передаете его функции iter() или используете его в цикле for. Попытка этого с помощью итератора просто вернет тот же исчерпанный объект итератора, который использовался в предыдущем проходе итерации, что делает его похожим на пустой контейнер.

Полужирный текст выше отвечает на ваши вопросы.

Когда вы

>>> [(i,z) for i in [1,2] for z in zs_i] 
[(1, 3), (1, 4)] 

итератор zs_i истощается в первой итерации с для цикла. Таким образом, когда он снова используется в цикле for, второй раз, как показано в приведенной выше документации, поднимается StopIteration. Таким образом, для перерывов цикла, и он не обрабатывает его снова.

Но те же самые работы со списком возвращенного range, потому что, из вышеуказанной документации,

объекта-контейнера (например, как list) производит свежий итератор каждый раз, когда вы передать его в iter() или использовать его в цикле for.

Итак, когда вы передаете список циклу for на каждой итерации, он создает новый итератор, и именно поэтому он работает так, как вы ожидали.

4

itertools.ifilterfalse является генератор. Если вы потребляете все, то yield s, называя это list, после этого ничего не принесет.

В

[(i,z) for i in [1,2] for z in zs_i] 

zs_id истощается для i = 1. Когда i = 2, zs_i ничего не даст.

1

Этот ответ является дополнением к другим ответам, которые более подробно объясняют основные механизмы. Если вы хотите, чтобы это сработало, генератор необходимо воссоздавать несколько раз в понимании.

Один из способов будет инициализировать новый генератор для каждого прохода вложенной для петли:

>>> [(i,z) for i in [1,2] for z in itertools.ifilterfalse(lambda x: x in ys, xs)] 
[(1, 3), (1, 4), (2, 3), (2, 4)] 
Смежные вопросы