Building with builtins: range() with Efficient Code is one of the smartest ways to write fast, memory-efficient loops and sequences in Python. The range() function generates integer sequences lazily — it doesn’t create a full list in memory (unlike Python 2), so it’s perfect for large ranges, indexing, or repeating actions without wasting RAM. In 2026, mastering range() with loops, comprehensions, zip(), and enumerate() is still a core skill — it powers everything from simple counters to massive data processing and simulations.
Here’s a complete, practical guide to using range() efficiently: why it’s fast, common patterns, performance wins over lists, real-world examples, and modern best practices with type hints and alternatives.
range() creates a range object — a lightweight, lazy sequence of integers. It’s memory-efficient because it only stores start, stop, and step — not the full list of numbers. This makes it ideal for for loops where you need to iterate a fixed number of times or generate indices.
# Efficient: range() directly in loop — no list created
for i in range(1_000_000):
# Process i (constant memory)
pass
# Inefficient (Python 2 style, avoid in Python 3):
for i in list(range(1_000_000)): # Creates huge list in memory
pass
Use range() with for loops for simple repetition or indexing — it’s faster and uses almost no memory compared to creating a list first.
# Efficient indexing
data = ["apple", "banana", "cherry"]
for i in range(len(data)):
print(f"Index {i}: {data[i]}")
# Even better: use enumerate() for index + value
for i, item in enumerate(data):
print(f"Index {i}: {item}")
Combine range() with list comprehensions — concise and fast for generating numeric sequences or indexed data.
# Squares of 0 to 9
squares = [x ** 2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Filtered: even numbers doubled
evens_doubled = [x * 2 for x in range(1, 21) if x % 2 == 0]
print(evens_doubled) # [4, 8, 12, 16, 20, 24, 28, 32, 36, 40]
Real-world pattern: parallel iteration with zip() and range() — avoid manual indexing by zipping sequences directly.
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
# Inefficient: manual range indexing
for i in range(len(names)):
print(names[i], scores[i])
# Efficient: zip() — no range needed
for name, score in zip(names, scores):
print(name, score)
# With indices: enumerate() + zip()
for i, (name, score) in enumerate(zip(names, scores), start=1):
print(f"{i}. {name}: {score}")
Best practices keep range() code fast, clean, and memory-safe. Prefer for i in range(n) directly — never for i in list(range(n)) — it wastes memory. Use enumerate(iterable, start=1) when you need indices — clearer and faster than range(len(iterable)). Combine with zip() for parallel iteration — zip(range(n), data) or just enumerate(data). Add type hints for clarity — range or Iterator[int] — improves readability and mypy checks. Modern tip: for very large ranges, prefer generators or Polars lazy frames — range(1_000_000_000) is fine (lazy), but avoid materializing huge lists. In production, use range() for indexing, not for creating massive lists — pair it with enumerate() or zip() to avoid len() calls on large iterables. Avoid negative steps unless necessary — range(n, -1, -1) for reverse is fine, but list reversal (reversed()) is often clearer.
range() with efficient code is Python’s go-to for fast, low-memory numeric sequences and indexing. In 2026, use it directly in loops, with enumerate() for positions, zip() for parallel work, and type hints for safety. Master range(), and you’ll write loops and sequences that are fast, clean, and scalable — from small scripts to big data processing.
Next time you need to loop a fixed number of times or index a sequence — reach for range(). It’s Python’s cleanest, most efficient way to generate numbers on demand.