Need help understanding this while loop

I’m new to while loops, and I don’t quite get what’s going on in this code from my book:

current_number = 1
while current_number <= 5:
    print(current_number)
    current_number += 1

After just watching a video on YouTube, current_number += 1 seems to be an escape from an infinite loop, which I saw from experimenting, but I didn’t understand why I couldn’t put that code before print.

As I was typing this I just realized that print comes first because there’s supposed to be five prints (though still don’t know for sure actually lol). I still don’t get why if I put current_number += 1 before print the returned values are “2, 3, 4, 5, 6”. How does it get to 6?

Response:

current_number = 1         # Initialize test variable 
while current_number <= 5: # Condition statement to satisfy in order to execute while loop body
    print(current_number)  # Print the value of variable
    current_number += 1    # Increment variable

After incrementing the variable, it goes back to the conditional statement to test if the condition is still met. If it is, it will execute the statements in the body. If the condition is not satisfied, it will go out of the while loop and continue to the next statement after the while loop body.

By adding 1 to 5.

Once the test variable current_number reaches 6, it is applied to the conditional statement (just like every previous value):

current_number <= 5

is 6 less than or equal to 5? The answer is NO. Thus, it does not execute the body statements and exits the while loop.

In case that is what is causing the confusion, the condition is not checked all the time, but only before the first statement in the loop. Therefore, if the value is incremented before the print statement, the condition will only be checked after the value is printed. If you think through the code, let’s suppose we have the above, just starting at a later number for brevity:

current_number = 5
while current_number <= 5:
    print(current_number)
    current_number += 1

current_number is <= 5, so the loop runs, 5 is printed and then current_number is incremented to 6. Now the condition is checked again, and as the condition 6 <= 6 is false, the loop terminates. By contrast, with

current_number = 5
while current_number <= 5:
    current_number += 1
    print(current_number)

…everything happens as before, except the number is incremented to 6 before the print, and thus 6 is printed.

These sort of off by one errors are particularly common with while loops if you’re not careful, which is one reason why in real life you’d always use a for loop for this sort of iteration, where you have a sequence of things and you want to iterate a fixed number of times:

for current_number in range(1, 6):
    print(current_number)

This is both shorter and avoids the above type of “off by one” errors due to the order of the action and the incrementing.

4 Likes

Thanks for the helpful replies.

I’ve been reading these posts and am trying to make this more clear. Is the reason why the increment variable is indented because it’s working directly with the print function even though it’s right after? It almost seems like it IS a print function. I know it’s not, but yeah. Maybe what I’m saying doesn’t make sense, but I think it’s good for me to put these thoughts down. Maybe other people think this too.

When you say “continue to the next statement after the while loop body”, you mean the increment variable, and not the print?

The increment variable statement (current_number += 1) is part of the while loop body, not the print statement.

Suppose you worked in a warehouse. Your boss told you to take inventory of all of the parts of a ‘A’ type that are currently available on hand. Once you’ve completed this task, move on to packaging parts of type `B’. So, you will not get to the 2nd task until the first task has been completed. This is in essence what I meant. After the while loop task has been completed, you can continue on to the next task. In your code, you did not state explicitly via code what the next thing to do was. What ever it is, continue to that.

Thanks for the helpful reply.

I understand what you’re saying when starting with current number = 5 instead of 1, however, when the value starts off at 1 in the original formula and the increment is placed before the print function, why isn’t the values returned just 2,3,4,5 instead of 2,3,4,5,6? I understand 1 being incremented to 2, but the condition says to terminate after 5.

I understand that, but I’m trying to figure out why if the increment variable is placed before print, the values returned goes from 2-6 instead of 2-5.

Statements are executed one after the other. A while loop as a whole executes a small group of statements repeated, until its condition is false. Within the loop, the statements are still executed one after the other.

Let’s rewrite the while loop with a hypothetical goto command. (This will be similar to how a loop is implemented in low-level code, like assembly language.) We’ll number the lines for ease of reference; the line numbers are not part of our hypothetical code.

1        current_number = 1
2 START: if not (current_number <= 5):
3            goto END  
4        print(current_number)
5        current_number += 1
6        goto START
7 END    ...

Tracing the execution of this code, we’ll see the following lines executed, in this order (with some comments):

1    # initialize current_number to 1
2    # first iteration
4    # output 1    
5
6
2    # second iteration
4    # output 2
5
6
2    # third iteration
4    # output 3
5
6
2    # fourth iteration
4    # output 4 
5
6
2    # fifth iteration
4    # output 5
5
6
2    # fifth iteration: current_value is now 6
3    # the comparison finally failed
7    # current_value == 6, but we didn't *print* this value.

If you were to swap lines 4 and 5, then you would see 2,3,4,5,6 instead of 1,2,3,4,5 as output, because you incremented each number after verifying it was less than 6 but before you printed the less-than-6 value.


The while loop gets rid of the need for (this use of) goto. It’s basically combines the if statement and the two goto statements into a single piece of code that restricts where control goes next. You can no longer jump to an arbitrary line of code; you can only jump to the statement after the loop or back to the beginning of the loop.

current_number = 1
while current_number <= 5:  # if not (current_number <= 5): goto END
    print(current_number)
    current_number += 1
    # goto START is implicit
...

Maybe if we add one more print statemen so that we follow the increment variable a bit more closely:

current_number = 1

while current_number <= 5:
    print('\ncurrent_number = ', current_number)
    current_number += 1
    print('current_number after increment = ', current_number)

Run the code, and study the output. Does it make more sense now?

You seem to think that this immediately triggers an escape. It doesn’t. It just eventually makes current_number large enough that the next time the while-condition is evaluated, the condition is false and the loop body isn’t entered again. But that only happens after the current execution of the loop body has completed.

2 Likes

I think you’re being misled by the meaning of the word “while” in English. Python isn’t English.

Python, and other programming languages, are their own thing with instructions and syntax. They are not natural languages, but using short, reasonably descriptive words from natural languages or abbreviations are a useful mnemonic.

Calling this kind of structure a “while” loop is close enough to its behaviour even if it isn’t precisely correct according to the English meaning of the word.

1 Like

That is because you’re printing after the increment happns instead of
before. See Paul’s suggestion:

 current_number = 1
 while current_number <= 5:
     print('\ncurrent_number = ', current_number)
     current_number += 1
     print('current_number after increment = ', current_number)

That should make clear that print() is printing the “current” value of
current_number. Notice the the value before the increment doesn’t go
from 2-5 as you mention above, but from 1-5. The values after the
increment go 2-6; one more across the board.

Each statement runs in sequence.

Would rendering the loop like this make the operations more clear?

 current_number = 1
 while True:
     print('\ncurrent_number = ', current_number)
     if not (current_number <= 5):
         # leave the loop
         break
     current_number += 1
     print('current_number after increment = ', current_number)

Ah I see. Thanks.

Ok. So after studying code from this thread and experimenting, please tell me if I got this right.

The current number is 1. I then set a conditional while loop that says I exit it out of the loop after 5. If I put the increment variable BEFORE the print function it’s saying this:

Pete: Hey, Phil. I need a few numbers printed out. The printer is set to start at 1, but I want only 2-6. There’s a +1 button, which will kick things off at 2. Set the cycle to end at 5, which will +1 that and end at 6. Then just hit the print button when it acknowledges what you want.

Phil: Why didn’t they just make it so you can just start at 2 and end at 6 without hitting the +1 button?

Pete: Those printer programmers have nothing better to do.

So unlike a range in a for loop in which (1,6) would be 1-5, (1,5) would be 2-6, if before print.

A visual might help.

while_loop

Note that a diamond is always a decision block and a square is always a statement block.

As you follow this diagram along, have a piece of paper and create two columns.
One labeled printed number and the other new number, where:

printed number = print(current_number)
new number = current_number += 1

For each iteration, write down the values. It should make sense.

Keep in mind that the user never ‘sees’ the value of the new number after incrementation. The user only sees what you allow them to see via the print statement.

4 Likes

You express a loop condition that skips the loop body if current_number is greater than 5. But that has nothing (in general) to do with the number of iterations. Consider this complete loop that runs for no iterations and prints nothing at all:

current_number = 6
while current_number <= 5:
    print(current_number)

or this one that prints “3” an infinite number of times:

current_number = 3
while current_number <= 5:
    print(current_number)

Python is an imperative programming language, which means each statement is executed one at at time, one after the other. That’s why comments like this:

I need a few numbers printed out.

Also seem off the mark. The print statement is only ever “told” to print one number. How many get printed depends on how many iterations of the loop happen. And that depends on how many times the condition current_number <= 5 is True when execution hits the top of the loop body.

@CozyBoat The real importance of the while loop comes up at an asyncio example (embedded into a GUI)
It’s actually part of the frame of any input/output and the logic for ending it is usually far away from its definition and may quite easily be a source for error in the logic of the app

from threading import Thread
import datetime
import asyncio

class Main():
    def __init__(self):
        self.cnt = -1
        RunAsync(self.async_gui)

    def async_gui(self, now):           # bridges async thread & Gui !!!
        self.cnt += 1
        if self.cnt < 6:
            print(f'{self.cnt+1}. {now}')
            return True
        return False

class RunAsync(Thread):
    def __init__(self, evh):
        super().__init__()
        self.evh = evh
        self.start()

    def run(self):
        asyncio.run(self.main())

    async def async_task(self):         # receives the stop
        return self.evh(
            f'{datetime.datetime.now():%a %b %d %Y  %H:%M:%S}')

    async def main(self):                   # scheduler
        while await self.async_task():
            await asyncio.sleep(1)

Main()

Thanks for the making the flowchart! I drew that and also created two columns. It does make it clear, but the thing I’m not sure about, though starting to maybe get, is the 2-6 thing.

Is it because printing only stops it from going until 5, but can’t stop the increment? Does putting it before print kick it off at 2 (since nothing is printed yet) then when it it hits 5, it still adds 1 to it, and ends at 6 because I’m not printing “current_number +1”, I’m just printing (current_number), which in both scenarios, whether the print function is before or after, ends at 5.

Maybe I just didn’t realize until now that current_number does NOT stop “current_number += 1” doing it’s own thing.

Edit: Hopefully I’m at least on the right track. After re-reading what I just typed, it sounds kinda contradictory with not printing “current_number =+ 1” because if I add another 1 to the code with print being last, then current_number seems like it should still end at 5, UNLESS it goes to 6 in that scenario because the 5 and 6 are a team at that point.