range() with step

The third argument, counting backwards, and why the stop value is never one of the numbers you get.

Overview

The three forms

range(5)         # 0 1 2 3 4
range(2, 6)      # 2 3 4 5
range(0, 10, 2)  # 0 2 4 6 8

One argument is the stop. Two are start and stop. Three add the step. The step can be any non-zero integer.

range_step.py

range_step.py Python 3
Output

                    

range_reverse.py

range_reverse.py Python 3
Output

                    

Worth knowing

Three arguments: range(start, stop, step). With one, it is the stop.
The stop is never produced. range(5) ends at 4.
Counting down needs a negative step and a stop below the start; to reach index 0, stop at -1.
reversed(x) or x[::-1] usually reads better than a backwards range.

range() with step: A Practical Guide

range takes up to three arguments — start, stop and step — and produces numbers without ever building a list. The two things worth internalising are that the stop is excluded, and what that means when counting down.

The stop is never included

range(5) gives five numbers ending at 4. This looks like an off-by-one waiting to happen and is the opposite: it is what makes range(len(items)) produce exactly the valid indices of a list, and what makes range(a, b) produce b - a numbers.

Counting down

Two things must both be true:

range(5, 0, -1)    # 5 4 3 2 1

The step is negative and the stop is below the start. Get one wrong and you get an empty range, not an error — range(5, 0) with no step produces nothing at all, because it is counting up from 5 to 0.

The classic mistake is stopping at 0 when you meant to include it:

range(3, 0, -1)   # 3 2 1  - misses 0
range(3, -1, -1)  # 3 2 1 0

To walk a list backwards by index you need range(len(items) - 1, -1, -1), which is three fiddly numbers in a row and exactly why the alternatives exist:

for x in reversed(items):
for x in items[::-1]:

Both say "backwards" without arithmetic. Reach for a backwards range only when you genuinely need the index.

It does not build a list

range(1_000_000) stores three integers — start, stop, step — and computes each value on demand. It is a few dozen bytes whatever the size, which the page prints beside the list version for contrast.

That laziness is also why x in range(n) is fast: it does arithmetic rather than searching. It is the one in test on a sequence that does not scan.

Only integers

range refuses floats. For a fractional step, build the integers and divide, or use a library. range(0, 1, 0.1) is a TypeError, not a rounding problem.

Check yourself

0 of 3

Answer without scrolling back up.

  1. What does `list(range(5, 0))` give?

  2. To walk indices of a 4-item list backwards including 0, you need:

  3. Why is `999_999 in range(1_000_000)` fast?

Cheat sheet

range() with step

range takes up to three arguments — start, stop and step — and produces numbers without ever building a list. The two things worth internalising are that the stop is excluded, and what that means when counting down.

PYTHON · vizlearn.in/python/range_step.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.