Переназначение значения итератора при итерации по нему - PullRequest
0 голосов
/ 02 ноября 2018

Прежде всего отказ от ответственности :

Я не хочу использовать подобный код, я знаю, что это плохая практика. Также меня не интересуют советы о том, как его улучшить, чтобы сделать это правильно. Меня интересует теория.

Как получается, что подобный код работает в Python 3.6:

ls = range(5)
for elem_a in ls:

    ls = range(5, 10)
    for elem_b in ls:
        print(elem_a, elem_b)

Я переназначаю значение ls, перебирая его. Сохраняется ли значение ls в первой итерации в памяти во время первого выполнения for elem_a in ls?

1 Ответ

0 голосов
/ 02 ноября 2018

Переназначение зацикливаемой переменной не имеет никакого эффекта, потому что переменная не переоценивается для каждой итерации. Фактически, цикл внутренне проходит по итератору , а не по вашему range объекту.

В основном, если у вас есть такой цикл:

seq = range(5)
for elem in seq:
    seq = something_else

Python переписывает это примерно так:

seq = range(5)

loop_iter = iter(seq)  # obtain an iterator
while True:
    try:
        elem = next(loop_iter)  # get the next element from the iterator
    except StopIteration:
        break  # the iterator is exhausted, end the loop

    # execute the loop body
    seq = something_else

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

Все это объясняется в документации составного оператора :

for_stmt ::=  "for" target_list "in" expression_list ":" suite
              ["else" ":" suite]

Список выражений вычисляется один раз; это должно привести к повторяемости объект. Итератор создан для результата expression_list. Затем набор выполняется один раз для каждого элемента, предоставленного итератор, в порядке, возвращенном итератором.

...