Python
range
xrange
Python 2.X
programming

What is the difference between range and xrange functions in Python 2.X?

Master System Design with Codemia

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

Introduction

In Python 2.x, range() and xrange() both represent arithmetic progressions, but they do it differently. range() builds a full list in memory immediately, while xrange() produces values lazily as you iterate. That difference matters mostly for memory usage and behavior on large ranges.

range() Creates a List

In Python 2.x, range() returns a real list.

python
values = range(5)
print(values)
print(type(values))

Output in Python 2 would be conceptually like:

text
[0, 1, 2, 3, 4]
<type 'list'>

Because the whole list exists immediately, range() is convenient if you truly need list behavior such as slicing, repeated reuse, or direct list operations.

xrange() Generates Values Lazily

xrange() returns an xrange object instead of a list.

python
1values = xrange(5)
2print(values)
3for number in values:
4    print(number)

The object computes numbers on demand during iteration rather than storing the whole sequence in memory at once.

This is why xrange() is usually the better choice for large loops in Python 2.x.

Memory Is the Main Difference

For a small loop, both functions are fine. For a very large loop, range() can consume a lot of memory because it materializes every integer immediately.

python
for i in xrange(1000000):
    pass

This is the classic Python 2 pattern for efficient iteration.

If you wrote the same loop with range(1000000), Python would first allocate a million-element list before the loop even begins.

Behavior Differences Beyond Memory

Because range() returns a list, it supports ordinary list operations directly.

python
values = range(10)
print(values[3])
print(values[2:5])

xrange() is iterable, but it is not just a normal list you can freely treat like one in every context.

That is the tradeoff:

  • 'range() gives list behavior'
  • 'xrange() gives memory-efficient iteration'

Python 3 Changed the Picture

This topic matters mostly for legacy code, because Python 3 removed xrange() entirely. In Python 3, range() behaves more like Python 2's xrange() than like Python 2's range().

So the migration rule is usually:

  • Python 2 xrange() becomes Python 3 range()
  • Python 2 range() often becomes list(range(...)) if a real list is needed

That is why many older articles treat xrange() as the "efficient loop" function: in Python 2, it really was.

When range() Still Made Sense in Python 2

Even in Python 2, range() was not wrong. It was appropriate when you explicitly needed a list object, such as when passing it to code that mutates or stores the sequence directly.

The important point is to choose it intentionally rather than by habit.

For legacy maintenance, that memory difference is often the only reason the distinction still matters. Old Python 2 loops that use range() on huge values can become much more expensive than they first appear.

Common Pitfalls

The biggest mistake is assuming Python 2 range() behaves like Python 3 range(). It does not. In Python 2, range() eagerly builds a list.

Another issue is using range() for very large loops in Python 2 and then being surprised by memory use. That is one of the exact cases xrange() was designed to improve.

People also sometimes treat xrange() as a drop-in list replacement. It is iterable, but it is not just a prebuilt list sitting in memory.

Finally, remember that this distinction is for Python 2.x. In modern Python 3 code, xrange() does not exist.

Summary

  • In Python 2.x, range() returns a full list.
  • In Python 2.x, xrange() returns a lazy iterable object.
  • 'xrange() is usually better for large loops because it uses far less memory.'
  • 'range() is useful when you actually need list behavior.'
  • In Python 3, range() mostly takes over the old role of xrange().

Course illustration
Course illustration

All Rights Reserved.