Moving back an iteration in a for loop

Question:

So I want to do something like this:

for i in range(5):
    print(i);
    if(condition==true):
        i=i-1;

However, for whatever reason, even though I’m decrementing i, the loop doesn’t seem to notice. Is there any way to repeat an iteration?

Asked By: hemlocker

||

Answers:

You have a misunderstanding about loops in Python. The for loop doesn’t care what you do with i at each iteration, because it is not related to the logic of the loop at all. Modifying i just rebinds a local variable.

You would need to use a while loop to achieve the behaviour you’re expecting, where the state of i does affect the control flow of the loop:

import random

i = 0
while i < 5:
    print(i)
    i += 1
    if random.choice([True, False]):
        i -= 1
Answered By: wim

Python loop using range are by-design to be different from C/C++/Java for-loops. For every iteration, the i is set the the next value of range(5), no matter what you do to i in between.

You could use a while-loop instead:

i = 0
while i<5:
    print i
    if condition:
        continue
    i+=1

But honestly: I’d step back and think again about your original problem. Probably you’ll find a better solution as such loops are always error-prone. There’s a reason why Python for-loops where designed to be different.

Answered By: Thorsten Kranz

Utilize a while loop:

i = 0
while i < 5:
    print(i)
    if condition:
        i -= 1
    i += 1

As has been mentioned, this is rather unidiomatic Python. Perhaps if you post what you are trying to achieve we can give some better advice.

Answered By: Yuushi

for loops in Python always go forward. If you want to be able to move backwards, you must use a different mechanism, such as while:

i = 0
while i < 5:
    print(i)
    if condition:
        i=i-1
    i += 1

Or even better:

i = 0
while i < 5:
    print(i)
    if condition:
        do_something()
        # don't increment here, so we stay on the same value for i
    else:
        # only increment in the case where we're not "moving backwards"
        i += 1
Answered By: Gabe

range(5) creates a list with numbers 0 thru 4 in it – [0, 1, 2, 3, 4].

When you run a for loop over it, you are iterating over the list. Doing i-= 1 will only decrement the value of that particular element of the list, and the iteration will continue.

Like the other answers here have suggested, you should use a while loop.

i= 0
while i<5:
    # do stuff
    if #condition:
        i-= 1 # or +
    i+= 1
Answered By: elssar

Repeating many other answers, and just for completeness, you will need to use a while loop.

i = 0
while i < 5:
    print(i)
    if (not condition):
       i+=1

If you want to move back an iteration in the loop (instead of repeating an iteration), then use this:

i = 0
while i < 5:
    print(i)
    if (condition):
        i -= 1
    else:
        i += 1

Essentially, while i < 5 evaluates upon each iteration, and checks if i < 5. Thus by decrementing/not changing i, we get something like this: (values of i)

Not changing: 1->2->3-(condition satisfied)> 3 -> 4 -> 5

Decrementing: 1->2->3-(condition satisfied)>2 -> 3 -> 4 -> 5

The reason why i=i-1 in your for loop doesn’t make it repeat the iteration is simple. In the for loop, i is assigned the value of the next item in the for loop. Python could care less about what you do with i, as long as it is able to assign the next item to it. Thus, the for loop for i in <your_iterable>:<do whatever> is closer to this:

_i = 0
_length = len(<your_iterable>)
while _i < _length:
    i = _i
    _i += 1
    <do whatever>

However, in this analogy, you wouldn’t be able to access the _ predicated variables (_i,_length). This is how I simplify the logic of the for loop. Note that regardless of what i is assigned to, it will be assigned to _i upon the next iteration, and the loop really doesn’t care about what i is.

Answered By: Snakes and Coffee

In Python it’s possible to set up a two-way exchange between an iterator (what comes after in in a for..in loop) and its consumer (code inside the loop). To achieve this, you can use send in the consumer code to “inject” a value in a generator. In your case, you can simply send back the current value once the condition is met and wrap the range call in a generator that repeats whatever is sent back to it. Here’s some code for you to play, intentionally verbose for clarity:

def repeateble(it):
    buf, it = None, iter(it)
    while True:
        if buf is None:
            # the buffer is empty, send them the next elem
            val = next(it)
        else:
            # there's something in the buffer
            # let's send that back
            val = buf

        # send the value and wait what they say
        back = yield val

        if back:
            # they've sent us something!
            # give them some dummy value as a result of send()
            yield None 
            # and save what they've sent in a buffer
            # for the next iteration
            buf = back

        else:
            # they haven't sent anything
            # empty the buffer
            buf = None


from random import randint

# create a repeateble generator
rng = repeateble(range(100))

for x in rng:
    print(x)

    # check "some condition"...
    if randint(1, 100) > 80:
        print('repeat:')
        # send the current value back to the generator
        # it will be returned on the next iteration
        rng.send(x)
Answered By: georg

You can use readlines if you’re iterating through a file and pull out the previous lines based on a condition.


with open("myfile.txt", "r") as f:
    text = f.readlines()
    for row in range(0, len(text)):
        if re.search("Error", text[row]):
            print(text[row-1].strip())
Answered By: user11494643
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.