Несколько итераторов (с использованием перечисления) для одной и той же итерации, что происходит? - PullRequest
5 голосов
/ 27 марта 2020

Рассмотрим следующий пример:

s = 'abc'
[(c1, c2) for j, c2 in enumerate(s) for i, c1 in enumerate(s)]

Вывод:

[('a', 'a'),
 ('b', 'a'),
 ('c', 'a'),
 ('a', 'b'),
 ('b', 'b'),
 ('c', 'b'),
 ('a', 'c'),
 ('b', 'c'),
 ('c', 'c')]

Я ожидал бы такой же вывод, если перечисление вызывается вне понимания списка и итераторы назначаются переменным:

it1, it2 = enumerate(s), enumerate(s)
[(c1, c2) for j, c2 in it1 for i, c1 in it2]

Но я получаю:

[('a', 'a'), ('b', 'a'), ('c', 'a')]

Что происходит? Я пользуюсь Python 3.6.9.

Ответы [ 3 ]

7 голосов
/ 27 марта 2020

Что происходит, так это то, что внутренний итератор исчерпан после первой итерации внешнего итератора:

s = 'abc'
it1 = enumerate(s)
it2 = enumerate(s)

for i, x in it1:
    for j, y in it2:  # <-- gets consumed when i = 0 and stays empty
        ...

Для контраста:

s = 'abc'

for i, x in enumerate(s):
    for j, y in enumerate(s):  # <-- gets recreated at each iteration
        ....

Если вам нужно постоянство , заключите его в list или tuple:

itr = list(enumerate(s))
print([(c1, c2) for j, c2 in itr for i, c1 in itr])
# [('a', 'a'), ('b', 'a'), ('c', 'a'), ('a', 'b'), ('b', 'b'), ('c', 'b'), ('a', 'c'), ('b', 'c'), ('c', 'c')]

, хотя обратите внимание на разный объем памяти при использовании enumerate() несколько раз по сравнению с list или tuple.

4 голосов
/ 27 марта 2020

Разница в том, что в:

s = 'abc'
[(c1, c2) for j, c2 in enumerate(s) for i, c1 in enumerate(s)]

A новый c1 перечислитель создается для каждого значения, полученного в первом for. В вашем втором примере используется тот же перечислитель (it2) - и он исчерпывается, когда достигает "c" - когда первый for переходит на следующую итерацию (c2 = "b") и пытается выполнить итерацию "it2", он уже исчерпан - и все выражение заканчивается.

1 голос
/ 27 марта 2020

перечисление встроенная функция возвращает iterator, когда элементы исчерпаны, она останавливается

в вашей первой версии, вы создаете новый enumerate(s) для каждой итерации первый l oop, а во втором l oop вы его потребляете,

во второй версии it2 завершил свои элементы с первой итерации первой l oop

s = 'abc'

def my_enumerate(s, name):
    print('a new enumerate: ', name)
    for i in enumerate(s):
        yield i
    print(f'the enumerate {name} is exhausted ')

[(c1, c2) for j, c2 in my_enumerate(s, 'it1') for i, c1 in my_enumerate(s, 'it2')]

вывод:

a new enumerate:  it1
a new enumerate:  it2
the enumerate it2 is exhausted 
a new enumerate:  it2
the enumerate it2 is exhausted 
a new enumerate:  it2
the enumerate it2 is exhausted 
the enumerate it1 is exhausted 
[('a', 'a'),
 ('b', 'a'),
 ('c', 'a'),
 ('a', 'b'),
 ('b', 'b'),
 ('c', 'b'),
 ('a', 'c'),
 ('b', 'c'),
 ('c', 'c')]

и для:

it1, it2 = my_enumerate(s, 'it1'), my_enumerate(s, 'it2')
[(c1, c2) for j, c2 in it1 for i, c1 in it2]

вывод:

a new enumerate:  it1
a new enumerate:  it2
the enumerate it2 is exhausted 
the enumerate it1 is exhausted 
[('a', 'a'), ('b', 'a'), ('c', 'a')]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...