CommonDocs.org

Loops and Iteration in Python

Make Python repeat itself with while loops, for loops and range(), break and continue, and nested loops, then build a number guessing game.

Part 6 of 6 in Intro to Python

Intro to Python: Section 6

Computers are really, really good at something humans are terrible at: doing the same boring task over and over again without complaining. Ask a person to write out every number from 1 to 1,000 and they’ll be bored before they hit 50. Ask Python, and it’s done before you can blink.

A loop is a block of code that repeats as long as some condition is met. Each single pass through the loop is called an iteration. When we say we’re iterating over something, we mean we’re stepping through it one piece at a time and running the same code on each piece.

You actually already saw a sneaky version of this in the last section. When the user typed an invalid option into the calculator, we had the main function call itself to start over. That works, but it’s not really what functions are for. Loops are the right tool for that job, and by the end of this section you’ll know how to use them.

There are two kinds of loops in Python: while loops and for loops. Let’s start with the while loop, since it builds right on top of the control statements you learned in section 4.

While Loops

A while loop repeats its body for as long as its condition is true. If an if statement is a fork in the road, a while loop is a roundabout. You keep going around and around until the condition tells you to take the exit.

1while conditional_statement:
2    # do this over and over

If that looks familiar, it’s because it’s almost identical to an if statement. The difference is that an if statement checks its condition once, while a while loop checks it again every time the body finishes.

Let’s write a loop that counts from 1 to 5.

python
1count = 1
2while count <= 5:
3    print("Count is", count)
4    count = count + 1
5print("Done counting!")
Terminal
Count is 1
Count is 2
Count is 3
Count is 4
Count is 5
Done counting!

Here’s what happened, step by step:

  • count starts at 1.
  • Python checks the condition. Is 1 less than or equal to 5? Yes, so the body runs. It prints “Count is 1” and then adds 1 to count, making it 2.
  • The body is done, so Python jumps back up to the top and checks the condition again. Is 2 less than or equal to 5? Yes, so the body runs again.
  • This keeps going until count becomes 6. Now the condition is false, so Python skips the body and moves on with the rest of the program.
  • Finally, “Done counting!” is printed. It isn’t indented, so it isn’t part of the loop. It only runs once, after the loop is finished.



The variable count is what’s called a counter, a variable whose whole job is keeping track of how many times something has happened. You’re going to write a lot of counters.

Just like with if statements, indentation is important. Every line you want repeated must be indented under the while statement. As soon as you remove the indent, you’re back outside the loop.

Shorthand Assignment Operators

Back in section 3, you learned that you can set a variable equal to itself and modify it, like my_variable = my_variable + 50. You’ll do this constantly with loops, so Python gives you a shortcut.

The += operator adds a value to a variable and stores the result back in that same variable. So count += 1 does exactly the same thing as count = count + 1. It’s just shorter to type. There are shorthand versions of the other arithmetic operators, too.

python
1score = 10
2score += 5
3score -= 3
4score *= 2
5print("Final score:", score)
Terminal
Final score: 24

The score starts at 10. It goes up by 5 to 15, down by 3 to 12, and then it’s doubled to 24. From here on I’ll be using += for counters, since that’s what you’ll see in most real Python code.

Infinite Loops

What happens if you forget to update the counter?

python
1count = 1
2while count <= 5:
3    print("Count is", count)

Since count never changes, the condition is always true and the loop never ends. This is called an infinite loop. Your terminal will fill up with “Count is 1” as fast as your computer can print it.

Terminal
Count is 1
Count is 1
Count is 1
Count is 1
Count is 1
...

If this happens to you, don’t panic! Click into the terminal and press Ctrl + C. That tells Python to stop the program right where it is. You’ll see a KeyboardInterrupt message, which is normal. It just means you stopped it. If you’re running it with the debugger in VS Code, you can also hit the red stop button.

Every programmer has written an infinite loop by accident, usually more than once. When your program seems frozen, the first thing to check is whether your loop’s condition can ever become false.

Looping Until the Input Is Valid

Now let’s fix up the calculator from the last section. Remember how we had main() call itself when the user typed something other than “add” or “subtract”? Here’s the same idea with a while loop instead.

python
1def main():
2    a = float(input("Type a number: "))
3    b = float(input("Type another number: "))
4
5    # keep asking until they type something we understand
6    operation = input('Would you like to "add" or "subtract"? ')
7    while (operation != "add") and (operation != "subtract"):
8        print("You did not choose a valid option. Try again.")
9        operation = input('Would you like to "add" or "subtract"? ')
10
11    if operation == "add":
12        print("The sum is:", a + b)
13    else:
14        print("The difference is:", a - b)
15
16if __name__ == "__main__":
17    main()
Terminal
Type a number: 5
Type another number: 9
Would you like to "add" or "subtract"? ad
You did not choose a valid option. Try again.
Would you like to "add" or "subtract"? subtrct
You did not choose a valid option. Try again.
Would you like to "add" or "subtract"? add
The sum is: 14.0

The while loop keeps asking for the operation as long as it’s not “add” and it’s not “subtract”. As soon as the user types one of the two, the condition becomes false, the loop ends and the program carries on.

So why is this better than calling main() again? A few reasons:

  • The user doesn’t have to re-type both numbers just because they made a typo in the operation.
  • Every time a function calls itself, Python has to remember where it left off. Do that too many times and Python gives up with a RecursionError. A loop can go around as many times as it needs to.
  • It’s easier to read. Anyone looking at the code can see right away which part repeats.

Note that we ask for the operation once before the loop, so operation has a value to check the first time around. Then we ask again inside the loop. You’ll see this pattern a lot.

For Loops and range()

While loops are great when you don’t know how many times you’ll need to repeat something. But a lot of the time you do know. You want to do something 5 times, or 100 times, or once for every letter in a word. That’s where the for loop comes in.

A for loop steps through a sequence of values, one at a time. Each time around, it stores the current value in a variable, called the loop variable, so you can use it in the body.

The easiest way to get a sequence of numbers is the range() function.

python
1for i in range(5):
2    print("Hello number", i)
Terminal
Hello number 0
Hello number 1
Hello number 2
Hello number 3
Hello number 4

Wait a minute. Why did it start at 0 and stop at 4? This trips up just about everyone at first. range(5) gives you five numbers, but it starts counting at 0 and stops before it reaches 5. Programmers love starting at 0, and you’ll see why when we get to lists.

Being wrong by one like this is called an off-by-one error, and it’s one of the most common bugs in all of programming. Don’t feel bad when you make one. Just check where your loop starts and stops.

Note that I didn’t have to create a counter or add 1 to it. The for loop handles all of that for me. The variable i is created by the loop and updated automatically on each iteration. You can name it anything you want, but i (short for index) is the traditional choice for simple counting loops.

If you want to start somewhere other than 0, give range() two arguments: the number to start at and the number to stop at.

python
1for i in range(1, 6):
2    print(i)
Terminal
1
2
3
4
5

The start number is included, and the stop number is not. So range(1, 6) gives you 1 through 5.

You can also give it a third argument, the step, which is how much to count by each time.

python
1for i in range(2, 10, 2):
2    print(i)
Terminal
2
4
6
8

This one counts from 2 up to (but not including) 10, two at a time.

The step can even be negative, which lets you count backwards. Just remember the stop number still isn’t included, so to count down to 1 we stop at 0.

python
1for i in range(5, 0, -1):
2    print(i)
3print("Liftoff!")
Terminal
5
4
3
2
1
Liftoff!

Looping Over a String

range() isn’t the only thing you can loop over. Recall from section 3 that a string is a sequence of characters. Since it’s a sequence, a for loop can step through it one character at a time.

python
1for letter in "Python":
2    print(letter)
Terminal
P
y
t
h
o
n

On each iteration, letter holds the next character in the string. Let’s do something a bit more useful with that. This program counts the vowels in a word.

python
1word = input("Type a word: ")
2vowels = 0
3
4for letter in word:
5    if letter in "aeiouAEIOU":
6        vowels += 1
7
8print(word, "has", vowels, "vowels.")
Terminal
Type a word: Banana
Banana has 3 vowels.

There’s a new operator in there: in. The in operator checks whether one thing can be found inside another. letter in "aeiouAEIOU" is true if the letter is any one of those ten characters. I included both lowercase and uppercase vowels so a word that starts with a capital vowel, like “Apple”, still gets counted correctly.

Note how this combines things you already know. There’s an if statement nested inside a for loop, and a counter (vowels) that only goes up when the condition is true.

A Sneak Peek at Lists

Back in section 4, I mentioned there are better ways to handle a group of items than a long chain of or operators. Here’s a quick preview of one of them.

A list is a single variable that holds several values in order. You create one with square brackets, with a comma between each item.

python
1pie_fruits = ["apple", "pear", "pumpkin"]
2
3for fruit in pie_fruits:
4    print(fruit, "would be good in a pie.")
Terminal
apple would be good in a pie.
pear would be good in a pie.
pumpkin would be good in a pie.

The loop runs once for each item in the list, and fruit holds the current item each time. We’ll cover lists properly in a later section, so don’t worry about the details yet.

Remember the pie checker from section 4? With a list and the in operator it gets a lot shorter, and adding another fruit is as easy as adding it to the list.

python
1pie_fruits = ["apple", "pear", "pumpkin", "cherry", "blueberry"]
2fruit = input("Please enter a fruit: ")
3
4if fruit in pie_fruits:
5    print(fruit, "would be good in a pie.")
6else:
7    print("I don't think that would be good in a pie.")
Terminal
Please enter a fruit: cherry
cherry would be good in a pie.

There’s no loop written here, but behind the scenes the in operator is looping through the list for you, checking each item until it finds a match.

Accumulators

An accumulator is a variable that builds up a result as the loop runs. It’s a lot like a counter, but instead of adding 1 each time, you add whatever value you’re working with.

There’s a famous story about the mathematician Carl Friedrich Gauss. When he was a kid, his teacher supposedly told the class to add up every number from 1 to 100 to keep them busy for a while. Gauss found a clever shortcut and had the answer in seconds. We’re going to skip the clever part and let Python do the busy work.

python
1total = 0
2
3for number in range(1, 101):
4    total += number
5
6print("The sum of 1 through 100 is", total)
Terminal
The sum of 1 through 100 is 5050

total starts at 0. Each time around the loop, the current number is added to it. After the first iteration total is 1, after the second it’s 3 (1 + 2), after the third it’s 6 (1 + 2 + 3), and so on until all 100 numbers have been added up.

Note that total = 0 is outside the loop. If you put it inside, it would be reset to 0 on every iteration and you’d end up with just 100. Accumulators always get their starting value before the loop begins.

Here’s a more practical example. Let’s ask the user how many test scores they have, have them type each one in, and then print the average.

python
1def main():
2    count = int(input("How many test scores do you have? "))
3    total = 0
4
5    for i in range(count):
6        score = float(input("Enter a score: "))
7        total += score
8
9    average = total / count
10    print("Your average score is", average)
11
12if __name__ == "__main__":
13    main()
Terminal
How many test scores do you have? 3
Enter a score: 90
Enter a score: 85
Enter a score: 77
Your average score is 84.0

The for loop runs once for each score, so if the user says they have 3, it asks 3 times. Each score is parsed into a float and added to total. When the loop is done, dividing the total by the count gives us the average.

I didn’t actually use i inside the loop this time, and that’s fine! Sometimes you just need something to happen a certain number of times and don’t care which iteration you’re on.

Break and Continue

Sometimes you need to step outside the normal flow of a loop. Python gives you two keywords for that.

The break keyword ends the loop immediately, no matter what the condition says. The program jumps straight to the first line after the loop.

It’s often paired with while True. Since True is always true, the loop would run forever on its own, and the break is your way out. This is a handy pattern for menus and programs that should keep running until the user says to stop.

python
1def main():
2    while True:
3        command = input('Type a command ("hello", "joke" or "quit"): ')
4        if command == "quit":
5            print("Goodbye!")
6            break
7        elif command == "hello":
8            print("Well hello there!")
9        elif command == "joke":
10            print("Why do programmers prefer dark mode? Because light attracts bugs.")
11        else:
12            print("I don't know that command.")
13
14if __name__ == "__main__":
15    main()
Terminal
Type a command ("hello", "joke" or "quit"): hello
Well hello there!
Type a command ("hello", "joke" or "quit"): joke
Why do programmers prefer dark mode? Because light attracts bugs.
Type a command ("hello", "joke" or "quit"): dance
I don't know that command.
Type a command ("hello", "joke" or "quit"): quit
Goodbye!

The loop keeps asking for commands and responding to each one. When the user types “quit”, it says goodbye and break ends the loop. There’s nothing after the loop, so the program finishes.

The continue keyword is break’s little sibling. Instead of ending the whole loop, it skips the rest of the current iteration and jumps right to the next one.

python
1for number in range(1, 11):
2    if number % 2 == 1:
3        continue
4    print(number, "is even")
Terminal
2 is even
4 is even
6 is even
8 is even
10 is even

Recall that the modulus operator from section 3 gives you the remainder of a division. If a number divided by 2 has a remainder of 1, it’s odd. So for every odd number, continue skips the print statement and moves on to the next number. Only the even numbers make it to the print.

You could get the same result by checking if number % 2 == 0 and putting the print inside that if statement, or with range(2, 11, 2). Like I said in section 4, there’s usually more than one way to do something in Python. Pick whichever reads most clearly to you.

Nested Loops

Just like you can nest if statements, you can put a loop inside another loop. These are called nested loops. The inner loop runs all the way through, from start to finish, every single time the outer loop goes around once.

python
1for outer in range(3):
2    for inner in range(2):
3        print("outer:", outer, "inner:", inner)
Terminal
outer: 0 inner: 0
outer: 0 inner: 1
outer: 1 inner: 0
outer: 1 inner: 1
outer: 2 inner: 0
outer: 2 inner: 1

Here’s the logic if you need help following:

  • The outer loop starts with outer = 0.
  • The inner loop runs completely, with inner = 0 and then inner = 1.
  • The inner loop is done, so the outer loop moves on to outer = 1, and the inner loop runs completely all over again.
  • The same thing happens for outer = 2. Then the outer loop is done too.

3 outer iterations times 2 inner iterations gives us 6 lines of output. Keep that multiplication in mind, because nested loops add up fast. A loop of 1,000 inside another loop of 1,000 runs its body a million times!

A classic use for nested loops is printing a grid, like a multiplication table.

python
1for row in range(1, 6):
2    for column in range(1, 6):
3        print(row * column, end="\t")
4    print()
Terminal
1	2	3	4	5
2	4	6	8	10
3	6	9	12	15
4	8	12	16	20
5	10	15	20	25

There’s one new trick in here. By default, print() ends with a new line, so every print starts on a fresh row. Passing end="\t" tells print to end with a tab instead, so the numbers in a row line up next to each other. After the inner loop finishes a row, the empty print() on its own moves us down to the next line.

The outer loop picks the row, and the inner loop fills in each column of that row. row * column is the number that belongs in that spot of the table.

You can nest loops as deep as you want, but just like the giant nested if statement in section 4, I wouldn’t recommend going more than two or three levels deep. If you find yourself needing more, that’s usually a sign some of that code belongs in its own function.

Which Loop Should I Use?

You can solve most problems with either kind of loop, but each one has its strengths. Here’s a good rule of thumb:

  • Use a for loop when you know how many times you need to repeat, or when you’re stepping through something like a range, a string or a list.
  • Use a while loop when you don’t know how many times it’ll take, like waiting for valid input or running until the user types quit.

If you’re not sure, start with whichever feels more natural. You can always rewrite it later. That’s a normal part of programming, not a sign you did something wrong.

Assignment: Number Guessing Game

Time to put it all together! In this assignment you’ll build a number guessing game. The computer picks a secret number between 1 and 100, and the player keeps guessing until they get it. After each wrong guess, the game tells them whether they were too high or too low. When they win, it tells them how many attempts it took and asks if they want to play again.

This one uses just about everything from the series so far: input, parsing, control statements, functions and, of course, loops.

To pick a random number, you’ll use the random module. Just like the math module from section 5, you need to import it at the top of your file. Then random.randint(1, 100) gives you a random int from 1 to 100.

Instructions: Create a new Python file based on this template. Implement the play_round and ask_play_again functions according to the comments. The main function is already done for you.

python
1"""
2Number Guessing Game
3Implement the two functions below, then test the game.
4"""
5import random
6
7def play_round():
8    secret = random.randint(1, 100)
9    attempts = 0
10    print("I'm thinking of a number between 1 and 100.")
11
12    # implement me:
13    # keep asking for guesses until the user gets it right
14    # after each wrong guess, print "Too low!" or "Too high!"
15    # when they get it, print the number and how many attempts it took
16
17def ask_play_again():
18    # implement me:
19    # ask if they want to play again, and keep asking until they type "y" or "n"
20    # return True for "y" and False for "n"
21    return False
22
23def main():
24    print("Welcome to the Number Guessing Game!")
25
26    while True:
27        play_round()
28        if not ask_play_again():
29            break
30
31    print("Thanks for playing!")
32
33if __name__ == "__main__":
34    main()

Here’s what the program might look like when it’s finished. Your secret number will be different, since it’s random!

Terminal
Welcome to the Number Guessing Game!
I'm thinking of a number between 1 and 100.
Your guess: 50
Too low!
Your guess: 75
Too high!
Your guess: 62
Too low!
Your guess: 68
Too low!
Your guess: 71
Too low!
Your guess: 73
Too low!
Your guess: 74
You got it! The number was 74
It took you 7 attempts.
Play again? (y/n) n
Thanks for playing!

Notice how each of my guesses was halfway between the numbers that were still possible. If you play that way, you’ll never need more than 7 guesses to find a number from 1 to 100.

Hints

  • Unlike range(), random.randint includes both numbers you give it. random.randint(1, 100) can return 1 or 100.
  • Remember that input() returns a string. Parse the guess into an int before comparing it to the secret number.
  • A while True loop with a break when they guess correctly works nicely in play_round. You could also use a regular while loop that checks if guess != secret, just make sure guess has a value before the loop starts.
  • ask_play_again is a lot like the input validation example from earlier in this section.
  • Add 1 to attempts with attempts += 1 every time the player guesses.

If you finish early and want an extra challenge, try one of these:

  • Only give the player 7 guesses. If they run out, tell them what the number was.
  • If the player guesses a number below 1 or above 100, tell them it’s out of range and don’t count it as an attempt.
  • Have play_round return the number of attempts, and keep track of the player’s best score in main().

Don’t be discouraged if it takes a few tries to get it working. Run it, see what goes wrong and fix one thing at a time. That’s what programming really is.

End of Intro to Python

You’ve read the whole series.

That was part 6 of 6. Browse the rest of the topic, or go back a step.

More in Intro to Python Previous: Functions in Python
Was this guide useful?

Spotted a mistake? Select the sentence in the guide and choose “Suggest a fix”.

Comments

Found an error? instead, it reaches me faster.

No comments yet. Questions and corrections are welcome.