Подтвердить что ты не робот

Область действия переменной python для цикла

Вот код python im, имеющий проблемы с:

for i in range (0,10):
    if i==5:
        i+=3
    print i

Я ожидал, что результатом будет:

0
1
2
3
4
8
9

однако интерпретатор выплевывает:

0
1
2
3
4
8
6
7
8
9

Я знаю, что цикл for создает новую область для переменной в C, но не имеет понятия о python. Может ли кто-нибудь объяснить, почему значение i не изменяется в цикле for на питоне и каково средство для получения ожидаемого результата.

4b9b3361

Ответ 1

Цикл for повторяет все числа в range(10), то есть [0,1,2,3,4,5,6,7,8,9].
То, что вы изменяете текущее значение i, не влияет на следующее значение в диапазоне.

Вы можете получить желаемое поведение с циклом while.

i = 0
while i < 10:
    # do stuff and manipulate `i` as much as you like       
    if i==5:
        i+=3

    print i

    # don't forget to increment `i` manually
    i += 1

Ответ 2

Аналогия с кодом C

Вы представляете, что ваш for-loop в python подобен этому C-коду:

for (int i = 0; i < 10; i++)
    if (i == 5)
        i += 3;

Это больше похоже на этот код C:

int r[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (int j = 0; j < sizeof(r)/sizeof(r[0]); j++) {
    int i = r[j];
    if (i == 5)
        i += 3;
}

Таким образом, модификация i в цикле не имеет ожидаемого эффекта.

Пример разборки

Вы можете посмотреть разбор кода python, чтобы увидеть это:

>>> from dis import dis
>>> def foo():
...     for i in range (0,10):
...         if i==5:
...             i+=3
...         print i
... 
>>> dis(foo)
  2           0 SETUP_LOOP              53 (to 56)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (0)
              9 LOAD_CONST               2 (10)
             12 CALL_FUNCTION            2
             15 GET_ITER            
        >>   16 FOR_ITER                36 (to 55)
             19 STORE_FAST               0 (i)

  3          22 LOAD_FAST                0 (i)
             25 LOAD_CONST               3 (5)
             28 COMPARE_OP               2 (==)
             31 POP_JUMP_IF_FALSE       47

  4          34 LOAD_FAST                0 (i)
             37 LOAD_CONST               4 (3)
             40 INPLACE_ADD         
             41 STORE_FAST               0 (i)
             44 JUMP_FORWARD             0 (to 47)

  5     >>   47 LOAD_FAST                0 (i)
             50 PRINT_ITEM          
             51 PRINT_NEWLINE       
             52 JUMP_ABSOLUTE           16
        >>   55 POP_BLOCK           
        >>   56 LOAD_CONST               0 (None)
             59 RETURN_VALUE        
>>> 

Эта часть создает диапазон между 0 и 10 и реализует ее:

          3 LOAD_GLOBAL              0 (range)
          6 LOAD_CONST               1 (0)
          9 LOAD_CONST               2 (10)
         12 CALL_FUNCTION            2

В этот момент верхняя часть стека содержит диапазон.

Этот получает итератор над объектом в верхней части стека, т.е. диапазон:

         15 GET_ITER  

В этот момент верхняя часть стека содержит итератор над реализованным диапазоном.

FOR_ITER начинает итерацию по циклу, используя итератор в верхней части estack:

    >>   16 FOR_ITER                36 (to 55)

В этот момент верхняя часть стека содержит следующее значение итератора.

И здесь вы можете увидеть, что верхняя часть стека выбрана и назначена i:

         19 STORE_FAST               0 (i)

Так что i будет перезаписано независимо от того, что вы делаете в цикле.

Вот обзор стековых машин, если вы этого раньше не видели.

Ответ 3

А для цикла в Python фактически является циклом for-each. В начале каждого цикла i устанавливается в следующий элемент в итераторе (range(0, 10) в вашем случае). Значение i переустанавливается в начале каждого цикла, поэтому его изменение в теле цикла не изменяет его значение для следующей итерации.

То есть, цикл for, который вы написали, эквивалентен циклу while:

_numbers = range(0, 10) #the list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_iter = iter(_numbers)
while True:
    try:
        i = _iter.next()
    except StopIteration:
        break

    #--YOUR CODE HERE:--
    if i==5:
        i+=3
    print i

Ответ 4

Если по какой-то причине вы действительно захотите изменить add 3 на i, когда оно равно 5, и пропустите следующие элементы (это как бы продвижение указателя в элементах C 3), тогда вы можете использовать итератор и потреблять несколько бит от этого:

from collections import deque
from itertools import islice

x = iter(range(10)) # create iterator over list, so we can skip unnecessary bits
for i in x:
    if i == 5:             
        deque(islice(x, 3), 0) # "swallow up" next 3 items
        i += 3 # modify current i to be 8
    print i

0
1
2
3
4
8
9

Ответ 5

Я получаю reset каждую итерацию, поэтому на самом деле не имеет значения, что вы делаете с ней внутри цикла. Единственный раз, когда он делает что-либо, когда я равен 5, а затем добавляет 3 к нему. После того, как он будет назад, он вернет меня к следующему номеру в списке. Вероятно, вы захотите использовать while здесь.

Ответ 6

Цикл Python for просто зацикливается на предоставленную последовательность значений - подумайте об этом как "foreach". По этой причине изменение переменной не влияет на выполнение цикла.

Это хорошо описано в учебнике.

Ответ 7

it = iter(xrange (0,10))
for i in it:
    if i==4: all(it.next() for a in xrange(3))
    print i

или

it = iter(xrange (0,10))
itn = it.next
for i in it:
    if i==4: all(itn() for a in xrange(3))
    print i