94

Suppose I have a for loop:

for i in range(1,10):
    if i is 5:
        i = 7

I want to change i if it meets certain condition. I tried this but didn't work. How do I go about it?

martineau
  • 119,623
  • 25
  • 170
  • 301
drum
  • 5,416
  • 7
  • 57
  • 91

5 Answers5

144

For your particular example, this will work:

for i in range(1, 10):
    if i in (5, 6):
        continue

However, you would probably be better off with a while loop:

i = 1
while i < 10:
    if i == 5:
        i = 7
    # other code
    i += 1

A for loop assigns a variable (in this case i) to the next element in the list/iterable at the start of each iteration. This means that no matter what you do inside the loop, i will become the next element. The while loop has no such restriction.

Volatility
  • 31,232
  • 10
  • 80
  • 89
  • 4
    Is this the only way? Feels kind of messy. – drum Feb 09 '13 at 06:14
  • 30
    @drum: Wanting to change the loop index manually from inside the loop feels messy. – BrenBarn Feb 09 '13 at 06:15
  • 2
    With a `while`, at least, we don't mistake it for a "regular" loop iterating over the range it promises to be iterating over. – Anton Kovalenko Feb 09 '13 at 06:16
  • 3
    @BrenBarn some times messy is the only way – hungryWolf Aug 17 '18 at 23:33
  • @BrenBarn, it is very common in other languages; but, yes, I've had numerous bugs because of it – Thomas Dec 19 '19 at 17:33
  • When the problem domain requires this kind of mess, often it makes more sense to create an iterable with only the appropriate values, like `for i in [1, 2, 3, 4, 7, 8, 9]:` (or any of a number of other ways). "Explicit is better than implicit", as they say, and "special cases aren't special enough to break the rules". Better to show only which indices you *are* using than which ones you aren't. – Karl Knechtel Aug 11 '22 at 06:29
35

A little more background on why the loop in the question does not work as expected.

A loop

for i in iterable: 
    # some code with i

is basically a shorthand for

iterator = iter(iterable)
while True:
    try:
        i = next(iterator)            
    except StopIteration:
        break
    # some code with i

So the for loop extracts values from an iterator constructed from the iterable one by one and automatically recognizes when that iterator is exhausted and stops.

As you can see, in each iteration of the while loop i is reassigned, therefore the value of i will be overridden regardless of any other reassignments you issue in the # some code with i part.

For this reason, for loops in Python are not suited for permanent changes to the loop variable and you should resort to a while loop instead, as has already been demonstrated in Volatility's answer.

timgeb
  • 76,762
  • 20
  • 123
  • 145
  • 2
    Great details. To help beginners out, don't confuse `the value of i will be overridden regardless` with `overriding the iterable`. If your `# some code with i` changes the iterable, it WILL have effects. – F.S. Dec 15 '19 at 19:15
4

This concept is not unusual in the C world, but should be avoided if possible. Nonetheless, this is how I implemented it, in a way that I felt was clear what was happening. Then you can put your logic for skipping forward in the index anywhere inside the loop, and a reader will know to pay attention to the skip variable, whereas embedding an i=7 somewhere deep can easily be missed:

skip = 0
for i in range(1,10):
   if skip:
      skip -= 1
      continue

   if i=5:
      skip = 2

   <other stuff>
Nerf Herder
  • 416
  • 3
  • 11
  • 4
    Although skipping is an option, it's definitely not the appropriate answer to this question. According to the question, one should also be able go back and forth in a loop. The accepted answer tackled this with a while loop. – drum Sep 07 '16 at 21:08
  • @drum if you need to do anything more complex than occasionally skipping forwards, then most likely the `i` values actually represent states in a FSM. There are definitely better ways to model that, although yes they generally will use a `while` loop to drive the FSM. – Karl Knechtel Aug 11 '22 at 06:34
0

Simple idea is that i takes a value after every iteration irregardless of what it is assigned to inside the loop because the loop increments the iterating variable at the end of the iteration and since the value of i is declared inside the loop, it is simply overwritten. You'd probably wanna assign i to another variable and alter it. For e.g,

for i in range(1,10):
    if i == 5:
        u = 7

and then you can proceed to break the loop using 'break' inside the loop to prevent further iteration since it met the required condition.

Pranav VS
  • 21
  • 1
  • 1
  • 3
0

Just as timgeb explained, the index you used was assigned a new value at the beginning of the for loop each time, the way that I found to work is to use another index.

For example, this is your original code:

for i in range(1,10):
    if i is 5:
        i = 7

you can use this one instead:

i = 1
j = i
for i in range(1,10):
    i = j
    j += 1
    if i == 5:
        j = 7

also, if you are modifying elements in a list in the for loop, you might also need to update the range to range(len(list)) at the end of each loop if you added or removed elements inside it. The way I do it is like, assigning another index to keep track of it.

list1 = [5,10,15,20,25,30,35,40,45,50]

i = 0
j = i
k = range(len(list1))
for i in k:
    i = j
    j += 1
    if i == 5:
        j = 7
    if list1[i] == 20:
        list1.append(int(100))
    # suppose you remove or add some elements in the list at here, 
    # so now the length of the list has changed
    
    k = range(len(list1))
    # we use the range function to update the length of the list again at here
    # and save it in the variable k

But well, it would still be more convenient to just use the while loop instead. Anyway, I hope this helps.