The range() built-in function returns an immutable sequence of integers generated from specified start, stop, and step parameters.
Immutable Sequence & Constant Memory: Unlike lists, a range object generates elements lazily on demand, occupying the same minimal memory footprint (around 48 bytes) regardless of whether it spans 10 numbers or 10 billion numbers. To iterate over collections with indices, see enumerate(). For measuring sequence lengths, see len(), or convert ranges to lists with list().
The range() constructor supports two invocation signatures:
# Formal syntax of built-in range()
range(stop)
range(start, stop[, step])
| Parameter | Type | Default | Description & Operational Rule |
|---|---|---|---|
start |
int |
0 |
The starting integer position of the progression (inclusive). |
stop |
int |
Required | The integer boundary where generation stops (exclusive; stops before this number). |
step |
int |
1 |
The step increment (or decrement) between consecutive values. Cannot be 0. |
| Return | range |
N/A | An immutable arithmetic sequence object implementing sequence protocols. |
Understanding argument defaults allows concise loop construction:
# 1. Basic invocations: single, two, and three arguments
# One argument: stop value (start defaults to 0, step defaults to 1)
r1 = range(5)
print("range(5) as list: ", list(r1))
# Two arguments: start and stop values (step defaults to 1)
r2 = range(2, 6)
print("range(2, 6) as list: ", list(r2))
# Three arguments: start, stop, and custom step increment
r3 = range(10, 30, 5)
print("range(10, 30, 5) as list:", list(r3))Terminal Output:
range(5) as list: [0, 1, 2, 3, 4]
range(2, 6) as list: [2, 3, 4, 5]
range(10, 30, 5) as list: [10, 15, 20, 25]
Setting a negative step allows counting backwards towards a smaller stop boundary:
# 2. Stepping backwards with negative increments
# Counting down from 10 to 1 with step=-2
countdown = range(10, 0, -2)
print("Negative step range: ", list(countdown))
# Traversing negative bounds
neg_range = range(-5, 5, 2)
print("Negative bounds range:", list(neg_range))Terminal Output:
Negative step range: [10, 8, 6, 4, 2]
Negative bounds range: [-5, -3, -1, 1, 3]
Because range stores only start, stop, and step parameters internally, it evaluates length, indexing, slicing, and membership testing in O(1) constant time:
# 3. O(1) Memory efficiency and constant-time operations
import sys
r_small = range(10)
r_huge = range(10_000_000_000)
print("Memory r_small (bytes):", sys.getsizeof(r_small))
print("Memory r_huge (bytes): ", sys.getsizeof(r_huge))
# O(1) membership testing via arithmetic calculation
print("Is 5_000_000 in r_huge?", 5_000_000 in r_huge)
print("Is 999_999_999 in r_huge?", 999_999_999 in r_huge)
# O(1) indexing and slicing
print("Element at index 100: ", r_huge[100])
print("Sliced sub-range: ", r_huge[10:20:2])Terminal Output:
Memory r_small (bytes): 48
Memory r_huge (bytes): 48
Is 5_000_000 in r_huge? True
Is 999_999_999 in r_huge? True
Element at index 100: 100
Sliced sub-range: range(10, 20, 2)
You can unpack parameters dynamically from lists or tuples using the splat operator (*):
# 4. Range parameter unpacking from lists and tuples
params = [5, 50, 10]
# Unpacking list arguments directly into range()
unpacked_range = range(*params)
print("Unpacked range elements:", list(unpacked_range))Terminal Output:
Unpacked range elements: [5, 15, 25, 35, 45]
Splitting large collections into fixed-size batches using step increments:
# 5. Production Recipe: Fixed-Size Batching & Chunking
def chunk_dataset(dataset: list, batch_size: int):
"""Splits a sequence into fixed-size chunks using range() stepping."""
batches = []
for i in range(0, len(dataset), batch_size):
batches.append(dataset[i : i + batch_size])
return batches
records = [f"Record_{i}" for i in range(1, 11)]
chunks = chunk_dataset(records, batch_size=3)
print("Total batches created:", len(chunks))
for idx, chunk in enumerate(chunks, 1):
print(f"Batch {idx}: {chunk}")Terminal Output:
Total batches created: 4
Batch 1: ['Record_1', 'Record_2', 'Record_3']
Batch 2: ['Record_4', 'Record_5', 'Record_6']
Batch 3: ['Record_7', 'Record_8', 'Record_9']
Batch 4: ['Record_10']Generating floating-point decimal progressions safely without cumulative rounding errors:
# 6. Production Recipe: Floating-Point Range Generator
def float_range(start: float, stop: float, step: float):
"""Generates floating-point numbers without rounding drift."""
count = int(round((stop - start) / step))
for i in range(count):
yield round(start + i * step, 6)
sample_floats = list(float_range(0.0, 1.0, 0.2))
print("Custom float range output:", sample_floats)Terminal Output:
Custom float range output: [0.0, 0.2, 0.4, 0.6, 0.8]
Avoid zero step values, passing float numbers, or creating unintentionally empty ranges:
# 7. Common traps: zero step, floats, and empty ranges
# Trap 1: Passing step=0 raises ValueError
try:
range(1, 10, 0)
except ValueError as err:
print("Zero step error: ", err)
# Trap 2: Passing float values raises TypeError
try:
range(0.5, 5.5)
except TypeError as err:
print("Float argument error:", err)
# Trap 3: Start >= stop with positive step results in an empty range
empty_r = range(10, 5)
print("Empty range length: ", len(empty_r))Terminal Output:
Zero step error: range() arg 3 must not be zero
Float argument error: 'float' object cannot be interpreted as an integer
Empty range length: 0
Practice number sequence generation, O(1) operations, and dataset batching in our interactive companion Jupyter Notebook:
Explore interactive code cells, benchmark O(1) range memory usage, test batching algorithms, and run self-validating assertions.
View on GitHub Run in Google ColabRun these unit checks directly in Python or your local environment to verify range() mechanics:
# 8. Self-verifying unit test suite for range()
# Test 1: Length and sequence matching
assert list(range(5)) == [0, 1, 2, 3, 4]
assert list(range(2, 6)) == [2, 3, 4, 5]
assert list(range(5, 0, -1)) == [5, 4, 3, 2, 1]
# Test 2: O(1) arithmetic operations
r = range(10, 1000, 5)
assert len(r) == 198
assert 55 in r
assert 56 not in r
assert r[2] == 20
# Test 3: Slicing returns a new range object
sub_r = r[2:6]
assert isinstance(sub_r, range)
assert list(sub_r) == [20, 25, 30, 35]
# Test 4: Range equality (same sequence of numbers)
assert range(0) == range(5, 5)
assert range(0, 3, 2) == range(0, 4, 2)
print("All range() unit assertions successfully verified!")Terminal Output:
All range() unit assertions successfully verified!
range is an immutable sequence type, not an iterator. It supports indexing, slicing, len(), and multiple iterations.
Python uses zero-indexed half-open intervals [start, stop), so len(range(n)) == n and ranges chain naturally without gaps.
No. range() strictly requires integers. For float sequences, use a generator or numpy.arange().
Wrap the range object with list(): numbers = list(range(10)).
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.