Reference

Reference · Loops

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.

XCODXReference · Updated

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-efficientrange(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

ParameterRequiredDefaultDescription
startNo0The first value of the sequence — the number counting begins at. Included in the output.
stopYesThe value counting stops *before*. It is exclusive: the sequence never contains stop itself. With a single argument this is the value supplied.
stepNo1The 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 by step, and stops before stop (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 as range(3).
  • Call list(range(...)) (or tuple(...)) 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.

ExpressionCostUse when
range(n)constant memory, lazyiterating in a for loop
list(range(n))builds all n integersyou 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?
No. 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?
Wrap it: 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?
Yes — give it a negative step: range(5, 0, -1) yields 5, 4, 3, 2, 1. The stop value is still excluded.
Why is range() memory-efficient?
It stores only its start, stop and step and computes each value on demand, so range(1_000_000) uses the same tiny amount of memory as range(3) — no list is ever built.