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.
Follow it step by step:
countis 3. Is3 > 0? Yes. Print 3, subtract 1.countis now 2.- Is
2 > 0? Yes. Print 2, subtract 1.countis now 1. - Is
1 > 0? Yes. Print 1, subtract 1.countis now 0. - Is
0 > 0? No. The loop ends andLiftoff!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:
savings = 1000
years = 0
while savings < 2000:
savings = savings * 1.07
years += 1
print(f"Your money doubles after {years} 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
forwhen you know what to loop over: the letters of a string, the numbers 1 to 10. - Use
whilewhen 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.
# 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:
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.
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:
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.
import random
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randint(1, 6))
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.
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 randomandrandom.randint()from the section above.while True:withbreakto play until the guess is right..isdigit()andcontinueto reject anything that is not a whole number without counting it as an attempt.if/elif/elsefrom 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¶
- Countdown. Ask the user for a starting number and count down from it to 1 with a
whileloop, then printLiftoff!. - Password gate. Keep asking for a password until the user types
python. Then print how many attempts they needed. - 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. - 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
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 isTrue, checking before each iteration.- Use
forwhen you know what to loop over,whilewhen 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:withbreakmeans "loop until I say stop".continueskips to the next iteration.import randomloads a module from the standard library.random.randint(a, b)gives a whole number fromatob.
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.