A Tool for Producing Numbers
range() does exactly one thing: it produces a sequence of integers. It's the go-to partner for for loops when you need to iterate a specific number of times or walk a numeric interval.
The simplest form takes a single argument — the stop value:
Output:
0
1
2
3
4
Two details that catch newcomers:
- Counting starts at 0.
range(5)gives you five numbers, starting from 0. - The stop value is exclusive.
range(5)stops before 5 — it doesn't include 5 itself.
These rules are consistent across Python's slicing, indexing, and range operations. Once you internalise "stop is exclusive," it stops tripping you up.
Three Forms
range() accepts one, two, or three arguments:
Step is the size of each jump. range(0, 20, 4) produces every fourth number. Keep that in mind the next time you need "every other item" or "every tenth sample."
Counting Down
The step can be negative, which lets you count backwards:
Again, the stop is exclusive — range(10, 0, -1) stops before 0, not at 0. To include 0, make the stop -1:
range Is Lazy
In Python 3, range() doesn't build a list of numbers in memory. It returns a lightweight range object that produces numbers one at a time, on demand. That's why you can write:
A billion-number "range" takes essentially no memory. The numbers are generated as the loop asks for them.
If you actually need a list of the numbers — because you want to modify it, sort it, or index into it beyond the basics — convert explicitly:
Most of the time, you don't need the list. for i in range(...) works directly.
Common Patterns
A handful of recipes you'll use constantly:
Do something n times.
The _ is a convention meaning "I don't care about this value" — Python doesn't enforce it, but any reader will recognize the intent.
Index into a sequence.
Prefer enumerate() over range(len(...)), but when you specifically need just the index:
Still, most of the time enumerate(items) is cleaner.
Iterate over every other element.
That prints a, c, e. For simple slicing, though, the slicing syntax is even more direct: items[::2].
Generate a quick numeric list.
That's a list comprehension — covered later — but range is what supplies the numbers.
What range() Is Not
Two sharp edges worth knowing:
rangeonly produces integers. If you need floating-point steps (like 0.0 to 1.0 in increments of 0.1), usenumpy.arangeor a loop with your own counter.rangedoesn't work with arbitrary iterables. It's specifically for integers. If you're trying to "range" over a list, you're probably looking forenumerateor just the list itself.
Putting It Together
A small example using several variations in one go:
Two nested range-driven loops, producing a grid. Notice that both loops start at 1 — range(1, 6) — because we want labels from 1 through 5.
Moving Into Collections
You've now seen conditions, both kinds of loops, and range. That's enough control flow to drive any collection. Next chapter: the collections themselves — lists, tuples, sets, and dictionaries.
Frequently Asked Questions
What does range() do in Python?
range() produces a sequence of integers. range(n) gives 0 through n-1. range(start, stop) gives integers from start up to but not including stop. range(start, stop, step) lets you choose the step size, including negative steps for counting down.
Does range() create a list?
No. In Python 3, range() returns a lightweight range object that generates numbers on demand. This is why range(10**9) is instant — it doesn't actually allocate a billion integers. Wrap it in list(...) if you really need a list.
How do I count down with range() in Python?
Use a negative step: range(10, 0, -1) counts 10, 9, 8, ..., 1. Remember that the stop value is always exclusive, so to include 0 you'd need range(10, -1, -1).