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.
Output in Python 2 would be conceptually like:
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.
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.
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.
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 3range() - Python 2
range()often becomeslist(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 ofxrange().

