2

Is it guaranteed that the iteration variable will always follow the original sequence no matter how you modify the variable itself?

The doc doesn't mention anything about this.

I've seen similar questions elsewhere but none of them gives authoritative answers. I've tested 100 times and it worked 100 times but I'm still not sure whether this is guaranteed. So please give some references.

Cyker
  • 9,946
  • 8
  • 65
  • 93

5 Answers5

4

Yes. It's totally safe to assign to the loop variable(s) in a for loop.

From The for statement docs:

The for-loop makes assignments to the variables(s) in the target list. This overwrites all previous assignments to those variables including those made in the suite of the for-loop:

for i in range(10):
    print(i)
    i = 5             # this will not affect the for-loop
                      # because i will be overwritten with the next
                      # index in the range

This information is also available via help('for')

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • It's safe to *assign* to the loop variable, but modifying it (if it is mutable) can affect the sequence. – Stuart Dec 10 '16 at 12:53
  • @Stuart Sure, but that normally won't affect the subsequent values taken by the loop variable(s), unless it's a weird iterable like the generator in your answer. – PM 2Ring Dec 10 '16 at 12:57
3

I agree with PM 2Ring's answer, but you ask if it is guaranteed the loop will "follow the original sequence". For an iterator defined by a generator function, this cannot be guaranteed, because the sequence may be altered in between iterations; and it is possible to alter the way a generator works by altering a mutable element in the sequence.

For example:

def strange_iter():
    x = {'value': 1}
    while x['value'] < 30:
        x = {'value': x['value'] + 1}
        yield x

for d in strange_iter():
    print(d['value'])
    if d['value'] == 10:
        d['value'] = 15     # loop will skip from 10 to 16

I'm not sure if such generators would have any use in practice or are best considered a bad practice to be avoided.

Stuart
  • 9,597
  • 1
  • 21
  • 30
1

The loop will use the original sequence, for example:

a = [1, 2,3 ]
for index,number in enumerate(a):
    print(a[index])
    index = 0
    number = 0

Output:

1
2
3
Ben
  • 138
  • 2
  • 9
0

I think the syntax

for item in stuff:
    ...

creates an iterator by calling iter(stuff), then values are extracted by calling next() on that iterator.

Gribouillis
  • 2,230
  • 1
  • 9
  • 14
-1

Short answer, It depends....

It depends on what you meant by safe. Technically there is nothing wrong modifying the variable used for iteration. When you iterate over a list or something, you're actually iterating over an iterator. It returns the value contained in the container (list, dict whatever)...

If you modify the variable returned by the iterator, you have to keep in mind about the mutability of the object you're trying to modify.

If you iterate over integers or strings, you're not actually modifying the variable but affecting a new value to the variable name.. Thus, you're not modifying the value contained in the container.

But if you're iterating over a container with mutable objects (let say dicts).. Modifying the variable by changing the content, will have an effect on the value contained in the container since they are the same values.

But doing something like this would have no effect at all on the value in the container as you're not modifying the value pointed by the variable name but changing to which value the variable name points to:

a = [{}, {}, {}]
for x in a:
    x = {'val': x}

While this will:

a = [{}, {}, {}]
for x in a:
    x['v'] = 1
Loïc Faure-Lacroix
  • 13,220
  • 6
  • 67
  • 99