Область действия переменной 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
на питоне и каково средство для получения ожидаемого результата.
Ответы
Ответ 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