Python3
xrange
Python programming
Python functions
Python2 vs Python3

Why is there no xrange function in Python3?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python 2 had both range() and xrange(), which made simple looping feel more complicated than it needed to be. Python 3 removed xrange() because the modern range() already provides the memory-efficient behavior that made xrange() valuable.

What xrange() Did in Python 2

In Python 2, range(5) created a full list in memory, while xrange(5) created a lightweight object that produced values on demand. That difference mattered when the sequence was large.

The duplication was awkward for everyday code. New Python users had to learn which function to pick, even though both represented the same concept: a sequence of evenly spaced integers.

Python 3 fixed that by redesigning range() itself. Instead of returning a list, range() now returns a lazy, immutable range object.

How Python 3 range() Replaced xrange()

The Python 3 version of range() behaves much more like old xrange(). It does not allocate every value up front, so iterating over a large interval remains cheap in memory.

python
1numbers = range(1_000_000)
2
3print(numbers[10])
4print(999_999 in numbers)
5print(len(numbers))

That code works without building a one-million-element list. The object knows its start, stop, and step, and calculates requested values when needed.

Python 3 range() also supports slicing:

python
1evens = range(0, 20, 2)
2subset = evens[2:6]
3
4print(list(subset))

Output:

text
[4, 6, 8, 10]

This is one reason Python 3 did not need both names. The new range() is efficient enough for iteration and still convenient for sequence-style operations.

Why the Language Became Simpler

Removing xrange() was part of a broader Python 3 cleanup. The language dropped duplicated tools when one clear design could cover the common case.

That simplification helps in three ways:

  1. Code is easier to read because there is one standard way to express integer ranges.
  2. Teaching becomes easier because learners do not need to memorize an old performance trick.
  3. Libraries can assume consistent behavior across codebases written for Python 3.

You can think of the change as "old xrange() won, but it kept the name range()."

Migrating Old Code

If you are moving Python 2 code to Python 3, most xrange() calls can be replaced directly with range().

python
for index in range(3):
    print(f"Processing item {index}")

If older code expected a real list, convert explicitly:

python
1positions = list(range(5))
2positions.append(5)
3
4print(positions)

Output:

text
[0, 1, 2, 3, 4, 5]

That explicit conversion is important because Python 3 range() is a sequence object, not a mutable list.

Here is a small example that shows the difference clearly:

python
1values = range(4)
2
3print(values)
4print(list(values))

Output:

text
range(0, 4)
[0, 1, 2, 3]

When you only need iteration, keep the range() object. When you need list methods such as append() or sort(), wrap it with list(...).

Performance and Memory Benefits

The design is especially helpful for loops over large intervals:

python
1total = 0
2
3for number in range(1, 1_000_001):
4    total += number
5
6print(total)

This loop does not materialize one million integers as a list before iteration begins. That keeps memory usage low and matches the original reason many Python 2 developers preferred xrange().

In practice, the Python 3 API is cleaner because performance-conscious code no longer needs a special function name.

Common Pitfalls

  • Expecting range() to behave like a mutable list. Use list(range(...)) if you need methods such as append() or item assignment.
  • Printing a range() object and expecting visible elements. Convert it with list(...) when you want the actual numbers displayed.
  • Porting Python 2 code mechanically and leaving xrange() in place. Python 3 raises a NameError because the function no longer exists.
  • Assuming removal means lost functionality. Python 3 kept the useful lazy behavior and attached it to range().

Summary

  • Python 2 had both range() and xrange(), mainly because they handled memory differently.
  • Python 3 removed xrange() and upgraded range() to be lazy and memory efficient.
  • Modern range() supports indexing, slicing, membership tests, and efficient iteration.
  • Use list(range(...)) only when you truly need a list.
  • The change simplified the language without removing the capabilities developers relied on.

Course illustration
Course illustration

All Rights Reserved.