Embercode
✕
LoopsLesson 1 of 16

45 min read

The for loop in Python

Why this matters

A program that does the same thing many times is common. A game draws every pixel of a frame. A shop adds up every item in a cart. A teacher's program averages the marks of thirty students. Without a way to repeat work, you would have to write the same line thirty times, and you could never handle a list that grows or shrinks.

A loop is the tool for repetition. The for loop is the first and most common loop in Python. Once you can read a for loop and predict exactly what it does, the while loop in the next lesson is a small step away. Everything in this module depends on one skill: looking at a loop and knowing how many times its body runs, what the variables hold on each run, and when it stops.

This lesson starts from the smallest idea and builds up. You will write small programs, predict their output before you run them, and trace them on paper. Tracing means writing down the value of each variable after each step, the way a teacher checks a student's working. It takes longer at first, and it is the quickest way to find the bug in a loop you wrote yourself.

The first principle: a computer repeats by jumping back

Before the syntax, look at what the processor does. A program is a list of instructions stored in memory. Normally the processor runs them one after another. A loop changes that order. At the end of the body, the processor jumps back to an earlier instruction, which is the start of the loop, and runs the body again. Each time it goes around, the run is called an iteration.

Jumping back is not enough by itself, because the program would then never stop. Every loop needs a rule that decides whether to go round once more. In a for loop, Python takes the next value from a sequence of values, and when no values are left, the loop ends.

So a counting loop has three moving parts:

  1. Something that starts the count.
  2. A check that says whether to go on.
  3. Something that moves the count to its next value.

Python's for loop does all three when you give it a range. That is why it is the easiest loop to read. The body is the code that repeats. In Python the body is the lines under the loop line, indented by four spaces. Indentation, which means the spaces at the start of a line, is how Python knows which lines belong to the loop.

An everyday analogy: laps on a track

Imagine a runner on a track who must complete five laps. The runner counts laps as they go. The count starts at zero. Before each lap, the runner asks, "Have I done fewer than five laps?" If the answer is yes, the runner runs one lap and adds one to the count. When the answer becomes no, the runner stops.

Look at how this maps to code:

  • Starting the count is the start of the run.
  • Asking "fewer than five?" before each lap is the check.
  • Adding one after each lap is the step to the next value.
  • Running the lap is the body.

The for loop is the best fit when you know in advance how many laps there are. You can see the number of laps in the range call, which is the part in round brackets.

The precise version: the parts of range

Here is the smallest useful for loop. It prints the word Round followed by a number, five times.

Python · editable

The output is:

Round 1
Round 2
Round 3
Round 4
Round 5

The text f"Round {i}" is an f-string. The letter f before the quote tells Python to put the value of i in place of {i}. You will use f-strings in most programs from here on.

Read the for line from left to right. The words range(1, 6) hold three numbers, separated by commas:

  • Start: 1 is the first value.
  • Stop: 6 is the value where the loop stops. The stop value is not included.
  • Step: the gap between values. When you leave it out, the step is 1.

The name i takes each value in turn. The line reads as "for each value i in this range". The line ends with a colon, which tells Python that an indented body follows.

Put in order, the steps are:

  1. Python takes the first value from the range, which is 1, and stores it in i.
  2. It runs the body. It prints Round 1.
  3. It takes the next value, 2, and stores it in i. It runs the body again and prints Round 2.
  4. It keeps going until the values run out. The next value after 5 would be 6, which is the stop value. The stop value is never produced, so the loop ends.

Notice the last step. The body ran for i equal to 1, 2, 3, 4 and 5, which is five times. The stop value 6 never went into the body. Remember this rule: a range runs up to the value before the stop value. Most off-by-one errors come from forgetting it. A Java programmer would write i <= 5 for the same loop. In Python, range(1, 6) says the same thing.

What the names mean

The name i is short for index or iterator, which is a common convention. You can use any valid name. round, step or count are fine too, as long as the name tells a reader what it counts. Good names make a loop read like a sentence.

Why the variable holds a whole number

Python does not ask you to declare the type of a variable. The values from range are whole numbers, so i is an int, which is Python's name for a whole number. You can check it:

Python · editable

The output is:

<class 'int'>
<class 'int'>

Python's int has no fixed size limit. The edge cases section below shows what that means for large totals.

Tracing a loop by hand

Tracing is the most useful habit in this module. You write the state of each variable in a table, one row per step, and you predict what the program will print before you run it. When your prediction and the real output disagree, you have found either a bug in the code or a gap in your understanding.

Here is a program that adds the numbers from 1 to 4. You do not have to run it to know the answer, but try tracing it first.

total = 0
for i in range(1, 5):
    total += i
print(total)

The notation total += i means the same as total = total + i. It adds the value of i to the value stored in total.

The name total is used on purpose. Python has a built-in function called sum. If you name a variable sum, the function is hidden for the rest of the program.

Here is the trace:

Iterationi at the start of the bodytotal after the body
110 + 1 = 1
221 + 2 = 3
333 + 3 = 6
446 + 4 = 10

After the fourth iteration, the loop has no more values, so it ends. The value of total is 10, and the program prints 10.

The table is worth keeping in your head as a habit. Each row has the same columns: the loop variable, and every other variable the body changes. A good tracer never skips a row. Most real bugs are found in the row where a value changed in a way the author did not expect.

Now make the trace visible. The next program prints the state after every iteration, so you can compare the output with your table.

Python · editable

The output is:

i=1 total=1
i=2 total=3
i=3 total=6
i=4 total=10
Final total: 10

Each line matches a row in the table.

Counting down and skipping numbers

The three parts of range are independent. You can change any of them to count in a different way.

To count down, give a negative step. The start is the high number, the stop is one below the low number you want, and the step is negative. The stop value is not included, so to reach 0 the stop must be -1.

Python · editable

The output is:

10
5
0
Go

The loop starts at 10. The next values are 5 and 0. The value after 0 would be -5, which is past the stop value -1, so it is not produced.

Here is the common trap. With a stop of 0, the 0 is never produced:

Python · editable

The output is:

10
5
[10, 5, 0]

The first loop printed only 10 and 5. The second line shows the values that range(10, -1, -5) makes, using list, which turns the range into a list you can print.

To skip numbers, use a step bigger than one. This loop visits 2, 6 and 10. Think about why 14 is not printed before you run it.

Python · editable

The output is 2, 6 and 10 on separate lines. The next value would be 14, which is past the stop value 11, so the loop ends.

A useful habit is to write the list of values before you write the body. list(range(10, -1, -5)) gives [10, 5, 0]. The list tells you how many passes the loop makes, which is three here, and whether the endpoints are included.

Worked example: a multiplication table from input

So far every value was fixed in the code. Real programs read their input from the user or from a file. The next program reads one number and prints its multiplication table from 1 to 5.

The function input() reads one line of text. Its result is always text, so int() changes the text into a whole number. If the user types 7, input() gives the text "7", and int() gives the number 7.

n = int(input())
for i in range(1, 6):
    print(f"{n} x {i} = {n * i}")

Input: 7

The output is:

7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35

The number of iterations is still fixed at five, because the range is range(1, 6). The value being multiplied comes from the user. The loop does not care where n came from. It uses whatever value is there.

Worked example: reading a list of numbers

Now the count itself comes from input. This is the most common pattern in exercises and interview problems. First you read how many values follow, then you run a loop that many times and read one value per iteration.

Input format: the first line is the count, and each value sits on its own line. input() reads one line at a time, so the values go on separate lines. Later in this module you will see how to read several values from one line.

Input:

4
10
20
30
40
n = int(input())
total = 0
for i in range(n):
    value = int(input())
    total += value
print(f"Count: {n}")
print(f"Total: {total}")

The output is:

Count: 4
Total: 100

Several details matter here. range(n) is the same as range(0, n). It makes the values 0, 1, 2 and 3 when n is 4, so the body runs exactly four times. Starting at zero is the usual choice when you are counting items, because the count of items already handled matches the value of i.

Each time through the loop, input() reads the next line. The variable value is set fresh on every pass. Its old value is replaced when the next line is read.

Worked example: a row of stars

A loop can build text too. The next program prints a row of stars whose length is read from input. The print function normally ends the line after it writes. The argument end="" changes that, so the stars stay on the same line. The last call to print() with no text moves to the next line.

Input: 6

width = int(input())
for i in range(width):
    print("*", end="")
print()

The output is:

******

The difference between print("*") and print("*", end="") is easy to forget. The first writes the text and then moves to a new line. The second writes the text and leaves the cursor at the end of the line. If you use the first form inside the star loop, you get one star per line, which is a common surprise for beginners.

What happens inside: how the loop gets its values

A range does not build a list of all its numbers before the loop starts. It makes each value when the loop asks for it. The loop asks for the next value on every pass. When there are no values left, the loop ends.

You can ask for the values one at a time with next. This shows what the for loop does for you:

Python · editable

The output is:

1
2
3
no more values

The function iter gives a helper object that hands out values one at a time. The function next asks it for the next value. When it has no more, it raises an error called StopIteration. A for loop catches that signal for you and ends. You do not need to write the try part yourself. It is shown here only to make the mechanism visible.

Because each value is made on demand, range(1, 1000000000) does not need a huge amount of memory. The loop makes one number at a time.

The loop variable stays after the loop

In Java, a variable declared in a for header is gone after the loop. Python does not work that way. The loop variable is an ordinary name in the program, and it keeps its last value after the loop ends.

Python · editable

The output is:

0
1
2
Last value: 2

The last value is 2, not 3. The loop never produced 3, because 3 is the stop value. This is different from Java, where the update runs once more before the test fails. In Python, the name is set only to the values the loop produces.

The one case to watch is an empty range. If the loop never runs, the name is never set:

Python · editable

The output ends with an error. The exact wording and marker lines change between Python versions, so your text may differ slightly:

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print("Last value:", i)
                         ^
NameError: name 'i' is not defined. Did you mean: 'id'?

Read the last line first. NameError means the program used a name that does not exist at that point. The error text may include a "Did you mean" hint, which is a guess from the names in the program and is not always right. The traceback lines above it show the file name and the line number, so you can find the use of the name. If you need the value after a loop that might not run, set the variable before the loop, such as i = -1.

A method for reading any for loop

Before you predict what a loop does, answer four questions in order. The method works for every for loop you will meet, and it is the method that helps when you explain a loop out loud.

  1. What is the first value? Read the start of the range.
  2. What is the last value that runs? Find the first value that the range would produce past the stop, and take the value before it.
  3. How does the variable change between passes? Read the step. The default is 1.
  4. What does the body do with each value? Read the body, and note which variables it changes.

Apply the method to this loop:

Python · editable

The first value is 3. The values are 3, 7 and 11. The next value would be 15, which is past the stop 12, so 11 is the last value that runs. The step is 4. The body prints the square of k, so the output is 9, 49 and 121, one number per line. Check each square: 3 times 3 is 9, 7 times 7 is 49, and 11 times 11 is 121.

Write your answer as a trace table, with one row for each value. If your answer and the real output differ, the mistake is in one of the four steps, and the question you answered wrongly tells you where to look. At first the method takes a few minutes per loop. After about twenty loops it becomes quick.

A worked example: a receipt total

Real programs often print a list and then a total. This program reads the number of items, then the price of each item. Prices are whole numbers of cents, so a price of 250 means 2.50 in money, and the program uses whole numbers only.

Input:

3
250
120
75
items = int(input())
total = 0
for k in range(1, items + 1):
    price = int(input())
    total += price
    print(f"Item {k}: {price}")
print(f"Total: {total}")

The output is:

Item 1: 250
Item 2: 120
Item 3: 75
Total: 445

Trace the loop. Pass 1 reads 250, so the total becomes 250. Pass 2 reads 120, and the total becomes 370. Pass 3 reads 75, and the total becomes 445. Then the range is done.

Notice range(1, items + 1). The stop value is not included, so the + 1 is needed to include the last item. Without it, range(1, items) would skip the last item. This is the same off-by-one rule as before, and it is the line most people check first.

Notice also that k is used only for numbering the lines, starting at 1, because people count items from 1. The loop itself is controlled by items, the count the user typed. A loop variable can serve two jobs: it can count the passes, and it can give a position in a list. In this program it does only the first. In the lists module, the position job becomes the main one, and positions start at 0, which is one reason many errors happen there.

A worked example: the largest value

Finding the largest value in a list is one of the most common single-loop patterns in exercises. The idea is simple. Keep the best value so far, and replace it when a larger value arrives. The tricky part is where to start. This program reads a count and the values, and it also records the position where the largest value first appears.

Input:

5
3
9
2
9
7
n = int(input())
largest = int(input())
position = 1
for k in range(2, n + 1):
    value = int(input())
    if value > largest:
        largest = value
        position = k
print(f"Largest: {largest}")
print(f"First position: {position}")

The output is:

Largest: 9
First position: 2

Read the first value before the loop, and start the loop at the second value. The first value is the first candidate for the largest, so it must be in largest before any comparison. Many beginners write largest = 0 instead. That works for lists of positive numbers, but it fails for a list such as -5, -2, -9. The answer is -2, and the program would print 0, which was never in the list. Here is that bug run on the same kind of input:

Python · editable

The output is 0, which is wrong for that input. Starting with a value from the list avoids the bug. The rule is to start the answer from a real element, not from a guess.

Trace the passes for the correct program. Position 1 starts with 3. Pass k equal to 2 reads 9. Since 9 is greater than 3, the program sets largest to 9 and position to 2. Pass 3 reads 2, which is not greater than 9, so nothing changes. Pass 4 reads 9 again. The test value > largest is false, because 9 is not greater than 9. So the position stays at 2. The rule uses the strict "greater than", which keeps the first occurrence. Pass 5 reads 7, which changes nothing. The output is 9 and position 2.

Python has a built-in function max that returns the largest value in a list. It is the right tool in real code. It does not tell you the position, and the loop shows the idea behind it.

The "first position" detail is the kind of question that interviewers add to the basic problem. Changing > to >= would change the answer to 4, the last occurrence, so the choice of comparison is part of the specification. Say which one you chose, and why, when you explain your code.

Questions people ask about the for loop

Several questions come up again and again. Here are the answers, with the reason for each.

Is range(5) the same as range(0, 5)? Yes. Both produce 0, 1, 2, 3 and 4. Pick the form that matches the problem. "Numbers 1 to 5" is range(1, 6). "Five items numbered from 0" is range(5).

Can the loop use decimal numbers? No. range takes only whole numbers, and a decimal start or step is an error:

Python · editable

The output ends with an error:

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    for x in range(0.0, 1.0, 0.1):
             ~~~~~^^^^^^^^^^^^^^^
TypeError: 'float' object cannot be interpreted as an integer

TypeError means a value had the wrong type for the operation. A float is Python's name for a decimal number. The fix is to count with whole numbers and divide inside the body:

Python · editable

The output is:

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Decimal numbers cannot always be stored exactly, so adding a decimal step many times can drift. This program adds 0.1 ten times and checks for 1.0:

Python · editable

The output is:

0.9999999999999999
False

The value is very close to 1.0 but is not exactly 1.0. Do decimal arithmetic in the body, and compare with a tolerance when you need to test a decimal result. The name _ is a convention for a value you do not use.

Is a for loop faster than a while loop? That is not the reason to choose one. Choose the form that reads best. For a known count, for with range is shorter, and it cannot forget the update, because Python makes the next value itself.

Why does my loop print nothing? Most often the range was empty from the start. Check the start and the stop together. For example, range(5, 3) makes no values, so the body never runs:

Python · editable

The output is done. That is correct behaviour, not a bug, and it matters for empty input.

Can I change the loop variable inside the body? Python allows it. It is still a bad habit for a counter, because the value is confusing to read. Here is what happens:

Python · editable

The output is 10 three times. The assignment i = 10 changes the name inside the body, and the next pass sets i from the range again. If you need to skip values, use a bigger step in range, or use continue, which you will meet in a later lesson.

Common mistakes

Most beginner loop bugs fall into a few groups. Learn to spot them, and you will save hours.

Off-by-one errors

An off-by-one error means the loop runs one time too many or one time too few. The cause is almost always the stop value. These two loops look almost the same:

Python · editable

The first line is 01234, which is five values. The second line is 012345, which is six values. Neither is wrong by itself. The question is what you need. If you are counting five items, use range(5). If you need the values from zero up to and including five, use range(6). Before you write the range, ask: is the last value I want included or not? If it is, the stop must be one past it.

Another common off-by-one is starting at 1 when you meant 0, or at 0 when you meant 1. Ask: does the first value correspond to the first item? Write the list of values the loop makes, using list(range(...)), and check the first and last numbers against the problem.

The loop never stops

A for loop over a range always ends, because a range has a fixed set of values. An infinite loop in Python usually comes from a while loop with no update, which the next lesson covers.

A for loop can still do nothing when the direction is wrong. The range below goes down from 0 toward 5, so it has no values:

Python · editable

The output is []. The loop body would run zero times, and the program would not show an error. Check the sign of the step against the direction you want.

A step of 0 is an error, because it would never move:

Python · editable

The output ends with:

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    for i in range(0, 4, 0):
             ~~~~~^^^^^^^^^
ValueError: range() arg 3 must not be zero

ValueError means the value had the right type but a value that range does not accept. The third argument to range is the step, so arg 3 is the step.

A missing colon or a wrong indent

Every for line ends with a colon. Leaving it out gives this error:

Python · editable

The output is:

  File "main.py", line 1
    for i in range(3)
                     ^
SyntaxError: expected ':'

The body must also be indented. Without the indent, Python reports this:

Python · editable

The output is:

  File "main.py", line 2
    print(i)
    ^^^^^
IndentationError: expected an indented block after 'for' statement on line 1

Both errors are about the shape of the code, not its meaning. Read the line number in the message, and look at the line before it.

Using input text as a number

input() always gives text. Adding text to a number is an error:

Python · editable

With input 5 and 6 on two lines, the output ends with:

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    total += input()
TypeError: unsupported operand type(s) for +=: 'int' and 'str'

The str in the message is text. The fix is total += int(input()).

Debugging walkthrough: a loop that prints too few lines

Here is a short program written by a learner. It should print the numbers from 1 to 10, one per line, but the output stops at 9.

for i in range(1, 10):
    print(i)

Step 1: read the symptom. The output shows 1 to 9. The requirement is 1 to 10. So the loop ends one value too early.

Step 2: look at the range. It is range(1, 10). The stop value 10 is not included, so 10 is never produced.

Step 3: decide the fix. You need 10 included, so the stop must be 11: range(1, 11). Writing range(1, 10 + 1) also works, and it shows the intent more clearly to a reader who thinks in terms of "up to 10".

Step 4: test the fix. Run the program and check the first and last lines. The first line is 1 and the last line is 10. Count the lines: there should be ten. A test is more reliable than a belief that the change works.

When a loop gives the wrong count and you cannot see why, add a print statement that shows the loop variable on every pass. Remove it after the bug is fixed. Professional developers use a debugger, which is a tool that pauses the program at a line and shows the value of each variable. The method is the same either way: observe the state, compare it with what you expected, and find the first point where they differ.

Edge cases you must think about

An edge case is an unusual input that the normal code might handle badly. Real programs fail on edge cases, so check them for every loop.

What if the loop should run zero times? A range with no values makes the body run zero times. list(range(1, 1)) is []. This is correct behaviour, and it is why for is safe for an empty list. If the input count is 0, the reading loop in the earlier example reads no numbers and prints a total of 0.

What if the input is negative? A range from 0 to a negative number has no values, so the loop does not run. list(range(-3)) is []. If negative values are invalid, check for them before the loop and print an error.

What if the total is very large? Java has a fixed-size int, and adding past its largest value silently wraps around to a wrong number. Python's int has no fixed limit. It grows to fit the number, so this loop gives the right answer:

Python · editable

The output is:

9223372036854775808
5000050000

The first line is larger than the largest 64-bit number. Python prints it without a problem. The second line is the sum of the numbers from 1 to 100000. Python's limit is the memory of your computer, not a fixed number of digits, so you do not need a special type for big totals.

What if the loop changes the stop value? A range is built once, when the loop starts. Changing n in the body does not change the loop:

Python · editable

The output is 0, 1 and 2. The range was built with the value 3, and the later change to n has no effect on the loop. This is different from a Java loop whose condition, such as i < n, is checked on every pass.

A small interview-style problem: the sum of 1 to n

An interviewer may ask you to add the numbers from 1 to n. The loop solution is short:

total = 0
for i in range(1, n + 1):
    total += i

It runs n times, so its time grows in step with n. Big O notation describes how the work grows as the input grows. The time here is O(n). The space used is a fixed amount for total and i, so the extra space is O(1). For now, read O(n) as "the work doubles when the input doubles."

A stronger answer mentions the formula n times (n + 1), divided by 2. For n equal to 100, the formula gives 5050 at once. The loop version still matters: it is easier to change if the rule changes, and it works for any operation that has no formula. Python also has sum(range(1, n + 1)), which does the loop for you.

Python · editable

The output is:

5050 5050 5050
5000050000 5000050000 5000050000

The three numbers on each line agree: the loop, the formula and the built-in sum. The // operator divides and drops the decimal part, so the formula gives a whole number. The operators module (module 03) explains // in more detail.

Practice

Try each exercise before you open the solution. Write a trace table for at least one of them. Each one is a chance to practise.

  1. Print the numbers 1 to 10 on separate lines.
  2. Print the even numbers from 2 to 20, one per line.
  3. Print a countdown from 5 to 1, then print Go. Print Go after the loop, not inside it.
  4. Read one number n and print the numbers from n down to 1. Test with input 4.
  5. Print the sum of all odd numbers from 1 to 99. Predict the answer before you run the code.
  6. Read one number n and print a row of exactly n stars on one line. Use a loop.
  7. Count how many numbers from 1 to 100 are divisible by 7. Hint: the remainder operator % gives 0 when a number is divisible.
  8. Print every third number from 0 to 30 using a single loop.
  9. The loop for i in range(0, 4, 0): is an error. Explain why, then write a loop that prints 0 to 3.
  10. Read a count n and n numbers, then print the largest of them. Start the largest value from the first number, not from zero. Test with input 4, then 3, 8, 2, 5.

Show the answer

Exercises 4, 6 and 10 read input. The others print directly.

# 1
for i in range(1, 11):
    print(i)
 
# 2
for i in range(2, 21, 2):
    print(i)
 
# 3
for i in range(5, 0, -1):
    print(i)
print("Go")
 
# 4
n = int(input())
for i in range(n, 0, -1):
    print(i)
 
# 5: the answer is 2500
total = 0
for i in range(1, 100, 2):
    total += i
print(total)
 
# 6
n = int(input())
for i in range(n):
    print("*", end="")
print()
 
# 7: the answer is 14
count = 0
for i in range(1, 101):
    if i % 7 == 0:
        count += 1
print(count)
 
# 8
for i in range(0, 31, 3):
    print(i)
 
# 9: a step of 0 is an error, because the loop would never move on.
# Fix: for i in range(4):
 
# 10
count = int(input())
best = int(input())
for i in range(1, count):
    v = int(input())
    if v > best:
        best = v
print(best)

What you now know

  • A for loop repeats its body once for each value in a range. Each run is an iteration.
  • range(start, stop, step) produces values up to the value before the stop. The stop value is never included.
  • Trace a loop with a table of the variables. Predict the output, then run.
  • The stop value decides whether the end value runs. Check it against the problem.
  • In Python, the loop variable keeps its last value after the loop, and an empty range leaves it unset.
  • Python integers have no fixed size, so totals do not wrap around. Check edge cases such as zero iterations.

The next lesson covers the while loop, which repeats while a condition holds when you do not know the count in advance.

Practice this topic: coding problems for Loops →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.