VIEW THIS AS

Auto mode follows the Route Engine until you choose a viewpoint.

YOU ARE HERE

ROUTE CHECK

CONNECTED TO

WHAT NEXT

Use the canonical route for this room, or HELP if you are unsure.

The Core Aim of Education | How to Avoid Off-by-One Mistakes in Python range() Loops

Did you know that a Python loop can run five times even though the largest number written in range(5) is 5? The answer is that Python does not include that upper endpoint as a value. A beginner who assumes a loop goes “up to and including” its last argument may miss the final item or create one extra iteration when fixing the mistake.

An off-by-one error happens when a program handles one more or one fewer element than intended. In Python, range(start, stop, step) generates a sequence beginning at start, advancing by step, and stopping before it reaches stop. The stop value is excluded. To include integer 5 while counting upward one at a time from 1, use range(1, 6); to visit five positions of a zero-indexed list, use range(5), producing 0, 1, 2, 3, 4.

Begin with the learner’s question: what will the computer actually print?

Instead of asking a beginner to memorise three parameter names, write for n in range(4): print(n) on paper and ask them to predict the numbers before running it. If they write 1, 2, 3, 4, there are two separate misunderstandings: the default start is 0, and the stop 4 is excluded. If they write 0 through 4, they understand the start but not the boundary. The diagnosis determines what to teach next.

This technique follows an important educational idea: a worked solution is useful only if the learner can say which rule produced each step. A screen showing the right output is feedback, not a substitute for an explanation. A good programming lesson teaches students to trace one iteration at a time and then check their prediction in Python.

The three parts of range()

  • Start: first included value. If only one argument is given, Python starts at zero.
  • Stop: exclusive boundary. A normal positive-step sequence stops before that value.
  • Step: how much to add each time. It defaults to +1, and it may be negative for counting down.
  • Number of values: depends on the distance between start and stop and the step size, not simply on the largest label.
  • Important limit: a zero step is invalid; it produces an error rather than an endless loop.

The word stop misleads students because ordinary language often uses “stop at six” to mean that six is the last included value. Python uses it as a boundary not included in the generated values. There is no contradiction: the programming definition is simply more precise. Teach students to annotate the endpoint with a small “excluded” mark during their first few traces.

Worked example: five observations in a school garden

Invented classroom scenario, not actual pupil data: a group plans five observation labels, Day 1, Day 2, Day 3, Day 4 and Day 5, for a garden project. They need to print each label exactly once. This example generates labels; it does not claim that any observation was actually taken or that a missing day’s reading can be fabricated.

A tempting instruction is for day in range(1, 5): print('Day', day). Hand tracing gives 1, 2, 3, 4. Only four labels appear; Day 5 is missing because stop 5 is not included. The correct version for exactly Day 1 through Day 5 is for day in range(1, 6): print('Day', day). Its output is Day 1, Day 2, Day 3, Day 4, Day 5. Count it aloud: five distinct labels, beginning at 1 and ending at 5.

Check the arithmetic independently: the included integers satisfy 1 ≤ day < 6. That is 1, 2, 3, 4, 5, exactly five values. A useful error check is to inspect first value, last value and total count. Students who examine only the first line may miss a boundary error.

What if the task is list indexing instead?

Now imagine a list with five illustrative labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']. Python’s list positions begin at 0, so they are 0, 1, 2, 3, 4. For indices, range(len(labels)) gives the intended five positions. Using range(1, 6) would skip position 0 and eventually try to access position 5, which does not exist in this five-element list. Better still, if a program needs the items rather than their positions, a direct loop for label in labels is often clearer.

The same range syntax serves two different stories: human-readable day numbers and machine-readable zero-based indices. Learners need to decide which set of values the problem requires before typing a bound. A formula copied from the previous exercise may be wrong even though it ran correctly there.

Second example: the step changes the count

Predict list(range(3, 10, 2)). Start at 3 and repeatedly add 2: 3, 5, 7, 9. The next value would be 11, outside the exclusive upper boundary of 10. Thus there are four values, not seven and not five. Python does not promise to land exactly on the stop number. It stops before crossing it.

For comparison, list(range(3, 9, 2)) gives 3, 5, 7: now 9 itself is the stop boundary, excluded. Students sometimes assume both produce the same set because the printed final limit differs by just one; seeing the last permitted number corrects that assumption. This small contrast is more effective than practising only easy range(5) examples.

Counting backwards

A negative step changes the direction. list(range(5, 0, -1)) gives 5, 4, 3, 2, 1. It excludes 0. By contrast, list(range(5, -1, -1)) gives 5, 4, 3, 2, 1, 0. If start is 4, stop is 1 and the step is +1, the sequence is empty because moving upward never approaches that lower stop boundary. Do not repair the example by adding random numbers: diagnose its direction first.

A five-minute loop-tracing diagnostic

  1. Prediction: Without running Python, write the entire sequence from range(2, 7). Correct: 2, 3, 4, 5, 6.
  2. Boundary: Circle the excluded value in range(2, 7). Correct: 7.
  3. Counting: Give the number of iterations of range(2, 7). Correct: five.
  4. Direction: Predict range(6, 1, -2). Correct: 6, 4, 2.
  5. Repair: A pupil wants 1, 2, 3, 4 inclusive and writes range(1,4). Correct the stop argument to 5, then justify it.

Mark the diagnosis by error type, not only total marks: default-zero misunderstanding, exclusive-stop misunderstanding, incorrect step, or failing to relate indices to list length. Two pupils with three correct answers out of five may need different explanations. That is why useful feedback names the cause of an error.

Practice from demonstration to independence

Stage one: annotate each argument

Give a single-argument range first, then a two-argument range, then a three-argument range. On each line, label the first included value, the next candidate value, the excluded boundary and the resulting count. Let pupils use pencil marks while building accuracy; remove the marks gradually rather than demanding fluent mental traces immediately.

Stage two: write the required sequence first

For a prompt such as “print every even number from 2 through 12,” the learner should write 2, 4, 6, 8, 10, 12 on paper before choosing range(2, 13, 2). In contrast, the code range(2, 12, 2) ends at 10. It prints five values rather than six. The explicit target sequence is the independent test that distinguishes good code from plausible-looking code.

Stage three: use an invariant

For a fixed loop over a five-element list, an elementary invariant is that every index visited remains between 0 and 4 inclusive. If the script ever attempts position 5, it has violated the intended list bounds. A beginner need not know the formal term invariant to benefit from writing a check such as “minimum index 0, maximum index 4, count 5.”

Stage four: change the context

Transfer the skill from garden-day labels to repeating a classroom task, drawing grid rows or visiting values in a table. Provide one exercise where the user-facing labels begin at 1 and another where computer positions begin at 0. A learner shows real progress when they select a suitable sequence without being told whether to add one to the stop bound.

Misconceptions, limitations and safer coding choices

“A larger stop always means one extra iteration.” Not with step sizes greater than one; a small change in stop may still exclude the same candidate values. Trace the sequence. “Range always produces a list.” In modern Python 3, range() returns a range object; wrapping it in list(...) makes the values visible as a list for demonstration. “Every loop should use range.” No: direct iteration over items is simpler when indices are not needed.

A correct-looking printed trace is not sufficient evidence that a larger program handles every input. Real code can receive empty lists, unexpectedly short data and values that fail validation. Students should test an empty case, a single-item case and the final boundary case. A missed observation should remain explicitly missing; changing a loop endpoint must not invent data.

What a teacher or parent can check

Observe whether the learner can independently predict the output of an unfamiliar range, justify its last included value and revise a failed boundary test. A parent who does not write Python can still ask: “What is the first value? Which value is excluded? How many passes?” Have the student run the code only after answering. At school, use peer explanation in pairs and then an individual exit ticket to distinguish shared success from independent understanding.

Keep three short dated examples as progress evidence: one with default start, one with a non-unit step and one that iterates over a list. Compare the reasoning after a later session, not just the immediate output. Gains vary by learner and practice; no number of exercises guarantees examination performance.

Frequently asked questions

Why does range(5) give 0, 1, 2, 3, 4?

The one-argument form has a default start of zero, a default positive step of one and an excluded stop of five. That makes five values with indices zero through four.

How do I include ten?

For a positive unit step from 1, write range(1, 11). For other starts or steps, first write the intended sequence and check that its final candidate is included while the next would be outside the boundary.

Is range(5, 5) an error?

No. With the default positive step it simply has zero values. This differs from range(5, 5, 0), which is invalid because a step of zero is not permitted.

Do I need range(len(items)) to visit a list?

Not unless you need indices or index-based comparison. for item in items is clearer for simple item-by-item work. If you need both position and item, enumerate(items) is often a good Python option.

Useful verified reading routes

For another example of maintaining a precise condition while checking a calculation, see how to catch mixed units in school spreadsheet formulas. The Computing study guide adds wider subject context. The eduKate Education Hub connects learning pathways, and the Clementi Mathematics tutorial reference illustrates diagnosis before progressive independent practice.

Python’s own control-flow tutorial for range() is the technical reference for the inclusive start, exclusive endpoint, stepping and common loop forms. All classroom examples and outputs in this article were constructed and hand-checked for explanation rather than presented as measured student results.

What this teaches beyond coding

A correct program depends on the exact boundary of a rule. Learning to predict its effects, check the last case and repair only the failing condition teaches a practical habit used in Mathematics, Science and daily decision-making. That independent habit is the core aim: understanding why the method works before trusting what the screen displays.