Преждевременное завершение генератора в понимании списка - PullRequest
0 голосов
/ 27 февраля 2019

Я использую генераторы в списках и получаю неожиданное поведение, когда один из генераторов заканчивается рано.Почему создание генератора вне понимания списка приводит к изменению поведения?

Генератор, который я создал, выглядит следующим образом:

def inc_range(a,b):
    for i in range(min(a,b), max(a,b) + 1):
        yield i

Первый способ вызова выглядит следующим образом:

[(i,j) for i in inc_range(1,3) for j in inc_range(4,6)]

Это дает мне следующий результат:

[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]

Второй способ его вызова следующий:

a = inc_range(1,3)
b = inc_range(4,6)

[(i,j) for i in a for j in b]

Это дает мне следующее:

[(1, 4), (1, 5), (1, 6)]

Экспериментируя, следующие два примера дали мне первый результат:

a = range(1,4)
b = range(4,7)

[(i,j) for i in a for j in b]
a = (i for i in range(1,4))
b = (i for i in range(4,7))

a = list(a)
b = list(b)

[(i,j) for i in a for j in b]

В то время как следующее снова дало мне второй результат.

a = (i for i in range(1,4))
b = (i for i in range(4,7))

[(i,j) for i in a for j in b]

Какое правило я нарушаю здесь относительно генераторов?Почему это имеет значение, когда я назначаю генераторы переменным перед их использованием в понимании списка, а не использую их напрямую?

ОТВЕТЫ

Проверьте следующееответы, которые помогли мне понять, что происходит здесь:

Алекс Ю mkrieger1

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

Чтобы получить желаемый результат, «внутренний» генератор должен был бы запускаться столько раз, сколько «внешний» генератор возвращает значение.

Но после первого запуска «внутренний» генераторисчерпан и не может быть запущен снова.

Добавление print иллюстрирует это (упрощение примера):

>>> def inc(a, b):
...    for i in range(a, b):
...        print(i)
...        yield i
...
>>> a = inc(1, 4)
>>> b = inc(4, 7)
>>> [(i, j) for i in a for j in b]
1  # <-- a begins to run
4  # <-- b begins to run
5
6  # <-- b exhausted here
2  # <-- a continued, but not resulting in list item, because lacking value from b
3
[(1, 4), (1, 5), (1, 6)]

Причина, по которой не сохраняются генераторы в переменных, работает, как ожидалось, потому чтоновый «внутренний» генератор создается для каждой итерации «внешнего» генератора.Опять же, проиллюстрировано некоторыми принтами:

>>> def inc(a, b):
...    print('started', a, b)
...    for i in range(a, b):
...        yield i
... 
>>> [(i, j) for i in inc(1, 4) for j in inc(4, 7)]
started 1 4
started 4 7
started 4 7
started 4 7
[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]

Причина, по которой использование range объектов или списков работает должным образом, заключается в том, что они могут повторяться произвольно много раз без исчерпания.

0 голосов
/ 27 февраля 2019

Генератор - это повторяемый объект, поэтому, когда вы вызываете его вне списка, он возвращает только следующий элемент.

a = inc_range(1,3)
b = inc_range(4,6)
c = inc_range(7,9)

[(i,j,k) for i in a for j in b for k in c]

Это даст только элементы для k в c при запуске

Следовательно, вам нужно перебирать все его объекты, как при определении его как массива.

[(i,j,k) for i in inc_range(1,3) for j in inc_range(3,6) for k in inc_range(7,9)]

Это заставляет генератор выдавать все значения в каждой из итераций.

...