Use Python for Loops with range()
Learn how Python range() controls for-loop iterations with start, stop, and step values, including counting patterns, descending loops, and common errors.
Python’s for loop repeats an indented block once for each item in an iterable. An iterable is an object that can provide values one at a time to a loop. The built-in range() function is commonly used when those values should be integers.
With range(), you can control how many times a loop runs and whether it counts upward, skips values, or counts downward.
For background on the general syntax of a Python for loop, review the linked lesson first if needed.
Basic for-loop structure
A for statement assigns each successive value from an iterable to a loop variable. The loop body is the indented block that runs during each iteration. An iteration is one execution of that loop body.
for number in range(3):
print(number)
In this example, number is the loop variable. It receives 0, then 1, then 2. The indented print() statement therefore runs three times.
0
1
2
What range() does
range() is a built-in constructor for an immutable, sequence-like range of integers. In modern Python, it returns a range object rather than immediately creating a list containing every number. The object generates or calculates values efficiently as they are needed.
When used in a for loop, each generated integer is assigned to the loop variable. This makes range(n) a convenient way to run a statement a fixed number of times.
for attempt in range(5):
print("Trying again")
The message is printed five times. The loop variable still receives the values 0 through 4, even though the example does not use that variable in the output.
range() argument forms
range() has three forms:
range(stop)range(start, stop)range(start, stop, step)
Start is the first included value. Stop is the boundary where the range ends, and is not included. Step is the amount added between values. If it is omitted, the step defaults to 1.
When only one argument is supplied, that argument is treated as stop, and start defaults to 0.
| Form | Start value | Stop boundary | Step value | Values produced |
|---|---|---|---|---|
range(5) | 0 | 5, excluded | 1 | 0, 1, 2, 3, 4 |
range(2, 6) | 2 | 6, excluded | 1 | 2, 3, 4, 5 |
range(1, 10, 2) | 1 | 10, excluded | 2 | 1, 3, 5, 7, 9 |
range(5, 0, -1) | 5 | 0, excluded | -1 | 5, 4, 3, 2, 1 |
Inclusive start and exclusive stop
The start value is included, but the stop value is excluded. This is the most important rule for avoiding off-by-one errors. An off-by-one error is a counting mistake caused by including or excluding one boundary value incorrectly.
for value in range(1, 5):
print(value)
This prints 1, 2, 3, and 4, not 5. To count from 1 through an inclusive final number such as 10, use a stop value one greater than that number:
for value in range(1, 11):
print(value)
The stop value is also useful for fixed iteration counts. range(5) contains five values, so the loop body runs five times. Its values happen to begin at zero.
Common loop-counting patterns
| Goal | Range expression | Sequence produced | Key point |
|---|---|---|---|
Repeat n times | range(n) | 0 through n - 1 | The count is n values. |
Count 1 through n | range(1, n + 1) | 1 through n | Add one to the exclusive stop. |
| Print odd numbers | range(1, n + 1, 2) | 1, 3, 5, ... | Start at 1 and increase by 2. |
| Print even numbers | range(2, n + 1, 2) | 2, 4, 6, ... | Start at 2 and increase by 2. |
| Count backward | range(n, 0, -1) | n through 1 | Use a negative step and a lower stop. |
Repeat an action a specified number of times
for i in range(5):
print("Action performed")
Use range(n) when the numeric value is only an iteration counter and the action should happen exactly n times.
Number iterations from 1 through 10
for i in range(1, 11):
print(f"This is iteration {i}.")
The message is printed ten times, with numbers 1 through 10. Here, i is used as meaningful output, not merely as an internal counter.
Using step values
The step is the difference between consecutive values. The default step is 1.
Skipping values with a positive step
for i in range(0, 11, 2):
print(i)
This prints the even values from 0 through 10. A step of 2 selects every other integer.
for i in range(1, 11, 2):
print(i)
This prints the odd numbers below 11: 1, 3, 5, 7, and 9.
Counting down with a negative step
A negative step subtracts from each value, creating a descending range. The start, stop, and step must point in the same direction: a start above the stop requires a negative step.
for i in range(10, 0, -1):
print(i)
The output counts down from 10 to 1. The stop value is still excluded, so 0 does not appear.
Output and the loop variable
The loop variable can be used in calculations, messages, or other statements inside the loop.
for number in range(1, 4):
square = number * number
print(f"{number} squared is {square}")
Choose a descriptive name when the number represents real data, such as day, page, or number. A name such as i is common when the value is only a simple counter.
After a loop finishes, its loop variable generally remains assigned in the surrounding scope:
for i in range(3):
pass
print(i) # 2
Although this behavior can be observed in ordinary Python code, do not make later code depend unnecessarily on a loop variable retaining its final value. Assign a separate variable when a result must be used after the loop.
Understanding direction and empty ranges
A positive step moves upward. A negative step moves downward. If the direction cannot reach the stop boundary, the range contains no values and the loop body does not execute.
for i in range(1, 10, -1):
print(i)
Nothing is printed because -1 moves downward from 1, while the stop boundary is above it at 10.
Likewise, range(10, 1) uses the default positive step. It cannot move upward from 10 toward 1, so it is empty. To count from 10 down through 1, write:
for i in range(10, 0, -1):
print(i)
Errors and common misconceptions
Confusing range(10) with range(1, 11)
range(10) produces 0 through 9. It runs ten times, but it starts at zero. Use range(1, 11) when the values themselves must be 1 through 10.
Assuming stop is included
range(1, 10) ends at 9. If the intended inclusive endpoint is 10, use range(1, 11). Always check the first value, last possible value, and number of iterations.
Using a zero step
range(1, 10, 0)
This raises ValueError because a zero step cannot make progress. Use a nonzero positive or negative step.
Passing decimals or text
Range arguments must be integers or objects that support integer conversion through __index__. Decimal values such as 2.5 and text such as "10" cause TypeError.
range(2.5) # TypeError
range("10") # TypeError
If input is expected to be a whole number, validate it and convert it with int() before calling range(). Do not use range() for arbitrary decimal increments; use a different approach designed for non-integer values.
Trying to modify a range
A range object is immutable, meaning its bounds and values cannot be changed in place. Create a new range when different bounds or a different step are needed.
values = range(5)
# values[0] = 10 # TypeError: a range cannot be modified
values = range(10) # Create a new range instead
Troubleshooting checklist
- The loop runs one fewer time: Check whether the stop value was mistakenly treated as included. Change
range(1, 10)torange(1, 11)when you need 1 through 10. - The loop begins at 0: The one-argument form defaults to a start of 0. Keep
range(10)for zero-based counting, or userange(1, 11)for labels beginning at 1. - A descending loop prints nothing: Supply a negative step and compatible bounds, such as
range(10, 0, -1). ValueErrorappears: Check for a step of zero.TypeErrorappears: Check that every range argument is an integer-compatible value rather than text or a decimal.
Key points
- A
forloop runs its indented body once for each value supplied by an iterable. range(stop)starts at 0 and stops beforestop.range(start, stop, step)includesstart, excludesstop, and moves bystep.- Use
stop = final_number + 1when the desired final number should be included in an ascending range. - Use a positive step to count upward and a negative step to count downward.
- A zero step is invalid, and range arguments must be integer-compatible.
Once these patterns are familiar, you can combine range() with conditions, nested loops, and other iterable techniques. For values that already exist in a list or string, iterate over that object directly instead of creating numeric indexes unnecessarily.