Skip to content

Repeating with while

Goal: repeat a block of code for as long as a condition stays true, and stop a loop early when you need to.

Why this matters

A for loop is perfect when you know in advance how many times to repeat: once per letter, once per number in a range. But many real programs do not know. Keep asking until the user types a valid answer. Keep playing until someone wins. Keep going until the money runs out. For those, Python has a second kind of loop: while.

The while loop

A while loop is an if that repeats. It checks a condition, and if the condition is True, it runs the body, then goes back and checks again. It only moves on when the condition becomes False.

while_countdown.py
count = 3

while count > 0:
    print(count)
    count -= 1

print("Liftoff!")
3
2
1
Liftoff!

Follow it step by step:

  1. count is 3. Is 3 > 0? Yes. Print 3, subtract 1. count is now 2.
  2. Is 2 > 0? Yes. Print 2, subtract 1. count is now 1.
  3. Is 1 > 0? Yes. Print 1, subtract 1. count is now 0.
  4. Is 0 > 0? No. The loop ends and Liftoff! is printed.

The shape is the same as if and for: a line ending in a colon, then an indented body. The difference from if is that the body can run many times. The difference from for is that there is no sequence, just a condition that is checked before every iteration.

You could have written this countdown with for count in range(3, 0, -1):, and for a fixed countdown that is the better choice. Here is a question a for loop cannot answer easily:

while_doubling.py
savings = 1000
years = 0

while savings < 2000:
    savings = savings * 1.07
    years += 1

print(f"Your money doubles after {years} years.")
Your money doubles after 11 years.

At 7 percent interest, how many years until 1000 becomes 2000? You do not know the number of iterations before you start. That is exactly when while is the right tool. The rule of thumb:

  • Use for when you know what to loop over: the letters of a string, the numbers 1 to 10.
  • Use while when you know when to stop: a condition, but not a count.

Infinite loops

A while loop ends only when its condition becomes False. If nothing inside the body ever changes the condition, the loop runs forever. This is called an infinite loop, and every programmer writes one by accident sooner or later.

forgot_update.py
# WARNING: this program never stops. Run it, watch it, then press Ctrl+C.
count = 3

while count > 0:
    print(count)

Run it. It prints 3 over and over, as fast as your computer can, and never stops. To stop a running Python program, click in the terminal and press Ctrl+C. Python prints KeyboardInterrupt and quits. Nothing is damaged. This is the normal way to stop a program that has run away from you.

The bug is that count -= 1 is missing, so count stays at 3 and count > 0 stays True forever. Whenever you write a while loop, ask yourself: what inside the body makes the condition eventually become False? If the answer is "nothing", you have an infinite loop.

Looping until input is valid

In Module 2, you learned to check input with .isdigit() before converting it. That version gave up after one bad answer. With while, you can keep asking:

until_valid.py
text = input("How many tickets? ")

while not text.isdigit():
    print("Please type a whole number.")
    text = input("How many tickets? ")

tickets = int(text)
print(f"Booking {tickets} tickets.")
How many tickets? abc
Please type a whole number.
How many tickets? 2.5
Please type a whole number.
How many tickets? 3
Booking 3 tickets.

Read the condition as "while the text is not made of digits". If the first answer is already valid, the body never runs, and the program goes straight to the booking. If not, it complains and asks again, as many times as it takes.

Notice that the input() line appears twice: once before the loop to get a first answer, and once inside it to get a new one. Without the second one, text would never change and you would have an infinite loop.

Leaving a loop early with break

Asking twice is a bit clumsy. There is another way to write "keep going until something happens": make the loop run forever on purpose, and jump out of it with break.

break_example.py
while True:
    name = input("Enter a name, or q to quit: ")
    if name == "q":
        break
    print(f"Hello, {name}!")

print("Goodbye.")
Enter a name, or q to quit: Ada
Hello, Ada!
Enter a name, or q to quit: Grace
Hello, Grace!
Enter a name, or q to quit: q
Goodbye.

while True: is a loop whose condition is always true, so on its own it would never end. The break statement ends the loop immediately, skipping the rest of the body, and the program continues with the first line after the loop. Here, that line prints Goodbye.

while True: with a break inside is one of the most common patterns in Python. It reads as "loop until I say stop". Just make sure the break can actually be reached, or you are back to an infinite loop.

Skipping one iteration with continue

break leaves the loop entirely. Its sibling continue skips the rest of the current iteration and goes straight to the next one. It works in both for and while loops:

continue_example.py
for number in range(1, 11):
    if number % 2 == 0:
        continue
    print(number)
1
3
5
7
9

number % 2 == 0 is the even-number test from Module 1. For even numbers, continue jumps back to the top of the loop before the print runs, so only odd numbers are printed.

You could write this with if number % 2 != 0: print(number) instead, and for a short body that is clearer. continue earns its place when the body is long and you want to deal with the "skip this one" cases at the top.

Using code other people wrote: import

For the project at the end of this lesson, the computer needs to pick a random number. Python cannot do that with anything you have seen so far, but it ships with a large collection of ready-made tools called the standard library. The tools are grouped into modules, and you bring one into your program with import.

random_demo.py
import random

print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randint(1, 6))
6
2
5

Your numbers will be different, because they are random. The first line, import random, loads the random module. After that, random.randint(1, 6) gives you a whole number from 1 to 6, both ends included, like rolling a die.

The dot works the same way as .upper() on a string: random.randint means the randint tool inside random. import lines go at the top of the file, before any other code, so it is obvious at a glance what a program uses.

You will meet many more modules in Module 8. For now, random is the only one you need.

Project: a number guessing game

You now know enough to write a real game. The computer picks a secret number from 1 to 100, and the player guesses until they get it, with a "too high" or "too low" hint after every wrong guess.

guessing_game.py
import random

secret = random.randint(1, 100)
attempts = 0
print("I am thinking of a number between 1 and 100.")

while True:
    text = input("Your guess: ")
    if not text.isdigit():
        print("Please type a whole number.")
        continue

    guess = int(text)
    attempts += 1

    if guess < secret:
        print("Too low.")
    elif guess > secret:
        print("Too high.")
    else:
        print(f"Correct! You got it in {attempts} attempts.")
        break
I am thinking of a number between 1 and 100.
Your guess: 50
Too high.
Your guess: 25
Too low.
Your guess: 37
Too high.
Your guess: 31
Correct! You got it in 4 attempts.

Everything in this program comes from a lesson you have done:

  • import random and random.randint() from the section above.
  • while True: with break to play until the guess is right.
  • .isdigit() and continue to reject anything that is not a whole number without counting it as an attempt.
  • if / elif / else from Module 1 to compare the guess with the secret.
  • The counter pattern from the last lesson to keep track of attempts.

Type it out yourself rather than copying it. Then play a few rounds. A good strategy is to guess the middle of the remaining range each time: 50, then 25 or 75, and so on. That finds any number in at most 7 guesses.

Try it

Change the guessing game so the range is 1 to 1000. Then play it and see how many guesses it takes with the middle-of-the-range strategy. Finally, make the game print the secret number if the player types give up.

Common mistakes

The program never stops

Nothing in the loop body changes the condition, or a while True: loop has no reachable break. Press Ctrl+C to stop it, then find the line that should be updating the variable.

The loop never runs at all

The condition was already False before the loop started. while count > 0: with count = 0 does nothing. Check the starting value of the variable in the condition.

SyntaxError: 'break' outside loop

break and continue only make sense inside a loop body. Check your indentation: the break must be indented under the while or for line.

NameError: name 'random' is not defined

You used random.randint() without import random at the top of the file. Every module must be imported before use.

AttributeError: module 'random' has no attribute 'randint'

This usually means you saved your own file as random.py, so import random loads your file instead of Python's module. Rename your file to anything else, and delete any __pycache__ folder that appeared next to it.

Exercises

  1. Countdown. Ask the user for a starting number and count down from it to 1 with a while loop, then print Liftoff!.
  2. Password gate. Keep asking for a password until the user types python. Then print how many attempts they needed.
  3. Running total. Ask the user for numbers, one at a time, until they type done. Ignore anything that is not a whole number, but keep going. At the end, print how many numbers were entered and their total.
  4. Limited guesses. Change the guessing game so the player gets only 7 guesses. After each wrong guess, say how many are left. If they run out, print the secret number.
Solution 1
start = int(input("Count down from: "))
count = start

while count > 0:
    print(count)
    count -= 1

print("Liftoff!")
Solution 2
secret = "python"
attempts = 0

while True:
    attempts += 1
    password = input("Password: ")
    if password == secret:
        print(f"Welcome! You needed {attempts} attempts.")
        break
    print("Wrong password, try again.")

The attempts += 1 sits at the top of the body so that every attempt is counted, including the successful one.

Solution 3
total = 0
count = 0

while True:
    text = input("Enter a number, or done to finish: ")
    if text == "done":
        break
    if not text.isdigit():
        print("That is not a whole number, skipping.")
        continue
    total += int(text)
    count += 1

print(f"You entered {count} numbers with a total of {total}.")

Both break and continue appear here. The order of the two if checks matters: done is not made of digits, so if the .isdigit() check came first, typing done would be skipped instead of ending the loop.

Solution 4
import random

secret = random.randint(1, 100)
attempts = 0
max_attempts = 7
won = False
print(f"I am thinking of a number between 1 and 100. You have {max_attempts} guesses.")

while attempts < max_attempts:
    text = input("Your guess: ")
    if not text.isdigit():
        print("Please type a whole number.")
        continue

    guess = int(text)
    attempts += 1

    if guess < secret:
        print(f"Too low. {max_attempts - attempts} guesses left.")
    elif guess > secret:
        print(f"Too high. {max_attempts - attempts} guesses left.")
    else:
        print(f"Correct! You got it in {attempts} attempts.")
        won = True
        break

if not won:
    print(f"Out of guesses. The number was {secret}.")

The loop condition is now attempts < max_attempts instead of True, so the loop ends by itself when the guesses run out. The won variable remembers whether the break happened, so the code after the loop knows which message to print. A variable that just records yes or no like this is called a flag.

Summary

  • while condition: repeats the body as long as the condition is True, checking before each iteration.
  • Use for when you know what to loop over, while when you know when to stop.
  • Something in the body must eventually make the condition False, or the loop is infinite. Ctrl+C stops a runaway program.
  • while True: with break means "loop until I say stop". continue skips to the next iteration.
  • import random loads a module from the standard library. random.randint(a, b) gives a whole number from a to b.

Module 3 complete

You can now make a program repeat itself, either a fixed number of times or until something happens, and you have written your first game. Module 4 introduces lists, so a program can keep track of many values at once.

Next: Lists