range()
range() produces a lazy, memory-efficient sequence of integers — the standard way to count in a Python loop. It never builds a list; it computes each value on demand from its start, stop and step.
Definition
range() returns an immutable range object representing an arithmetic sequence of integers. It is the idiomatic way to count in a for loop and to generate spans of whole numbers. The object is *lazy*: it does not store the numbers, it stores only its start, stop and step and computes each value as the loop asks for it.
Two properties make range() predictable. First, the sequence is half-open — it includes start and *excludes* stop, so range(0, 5) yields 0, 1, 2, 3, 4. That exclusivity is deliberate: it makes range(len(seq)) line up exactly with a sequence's valid indices and lets adjacent ranges tile without overlapping. Second, it is memory-efficient — range(1_000_000) occupies the same tiny footprint as range(3), because no list is ever built. To materialise the numbers into an actual list, wrap it: list(range(...)).
Syntax
range(stop)
range(start, stop)
range(start, stop, step)
Called with one argument, that argument is the stop and start defaults to 0, step to 1. With two arguments they are start and stop. With three, the last is the step — the gap between successive values, which may be negative to count downward. This one-argument-is-stop rule is unusual (most functions fill positional arguments left to right), so it is worth committing to memory: range(5) means *stop at 5*, not *start at 5*.
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
start | No | 0 | The first value of the sequence — the number counting begins at. Included in the output. |
stop | Yes | — | The value counting stops *before*. It is exclusive: the sequence never contains stop itself. With a single argument this is the value supplied. |
step | No | 1 | The difference between one value and the next. May be negative to count downward; may not be 0 (that raises ValueError). |
When step is positive the sequence runs upward from start and ends before it would reach stop; when step is negative it runs downward and ends before passing stop going the other way. If start and stop are already on the wrong side of each other for the given step — for example range(5, 0) with the default positive step — the range is simply empty, and the loop body never runs. No error is raised for an empty range; only a step of 0 is an error.
Return value
A range object — an immutable sequence of integers, *not* a list. It is lazy and reusable.
- The sequence starts at
start, advances bystep, and stops beforestop(the stop value is never included). - It supports indexing, slicing,
len(), membership tests (x in r) and reversal, all computed without building a list. - It occupies constant memory regardless of length —
range(10**9)is as cheap to create asrange(3). - Call
list(range(...))(ortuple(...)) to materialise the numbers into a concrete collection when you actually need one.
Examples
Every example below runs standalone.
Count from zero (single argument)
One argument is the exclusive stop; counting starts at 0.
for n in range(5):
print(n) # 0 1 2 3 4
print(list(range(5))) # [0, 1, 2, 3, 4]
Start, stop and step
Three arguments give a start, an exclusive stop, and the gap between values.
print(list(range(2, 11, 2))) # [2, 4, 6, 8, 10]
print(list(range(1, 10, 3))) # [1, 4, 7]
Counting down with a negative step
A negative step walks the sequence downward; stop is still exclusive.
print(list(range(5, 0, -1))) # [5, 4, 3, 2, 1]
An empty range never runs
When start and stop are on the wrong side for the step, the range is empty — no error, the loop body just never executes.
print(list(range(5, 0))) # [] (default step +1 can't go up to 0)
for _ in range(3, 3):
print("never prints")
print("done") # done
Sum, count and membership without a list
A range object answers sum(), len() and in directly — it computes the answer from its endpoints, so none of these builds a list.
r = range(2, 21, 2) # 2, 4, ..., 20
print(list(r)) # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
print(sum(r)) # 110
print(len(r)) # 10
print(min(r), max(r)) # 2 20
print(10 in r, 11 in r) # True False
Index and slice a range like a sequence
Ranges support indexing, negative indexing, slicing (which returns another range) and .index() — all in constant time, without materialising the numbers.
r = range(0, 100, 10) # 0, 10, ..., 90
print(r[0], r[3], r[-1]) # 0 30 90
print(list(r[2:5])) # [20, 30, 40]
print(r.index(50)) # 5
Reverse a range
Wrap a range in reversed() to walk it from the last value back to the first — still lazily, without building a list.
for n in reversed(range(1, 4)):
print(n) # 3, then 2, then 1
print(list(reversed(range(0, 10, 2)))) # [8, 6, 4, 2, 0]
Repeat an action a fixed number of times
When you need the loop body run N times but never use the counter, bind it to _ — the conventional throwaway name.
for _ in range(3):
print("tick") # tick, tick, tick
print("=" * 10) # ==========
Feed a comprehension and tile two ranges
range() is the usual driver for a list comprehension, and because adjacent ranges never overlap they concatenate cleanly.
squares = [n * n for n in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
evens = [n for n in range(0, 20) if n % 2 == 0]
print(evens) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
print(list(range(0, 3)) + list(range(3, 6))) # [0, 1, 2, 3, 4, 5]
Browser & runtime support
range() vs building a list
In Python 3, range() is always the lazy object — there is no separate xrange() (that was the Python 2 name for this behavior, and plain range() in Python 2 eagerly built a list). Because the modern range() never materialises its values, prefer it for iteration and reach for list(range(...)) only when you genuinely need a stored, mutable list.
| Expression | Cost | Use when |
|---|---|---|
range(n) | constant memory, lazy | iterating in a for loop |
list(range(n)) | builds all n integers | you need a real, indexable, mutable list |
Availability
range() is a built-in type in every version of Python 3, always available with no import required.
Frequently asked questions
Does range() include the stop value?
range() is half-open: it includes start and stops *before* stop, so range(0, 5) yields 0, 1, 2, 3, 4 and never 5.How do I turn a range into a list?
list(range(5)) gives [0, 1, 2, 3, 4]. range() itself is lazy and does not build a list until you ask it to.Can range() count backwards?
range(5, 0, -1) yields 5, 4, 3, 2, 1. The stop value is still excluded.Why is range() memory-efficient?
range(1_000_000) uses the same tiny amount of memory as range(3) — no list is ever built.