Difference Between range and xrange in Python

The range() and xrange() functions are used to create iterables or generators of iterable. The two functions have the same arguments, but they are fundamentally different in other ways.

Compatibility with Different Python Versions

The range() function is available in Python 2 and Python 3, but xrange() is only available in Python 2.

Return Type and the Basic Structure

The return type and the basic structure of the objects generated by the two functions depend on your Python version.

In Python 2:

  • range() creates a list. That means if you do range(1, 100), the function returns a list of 99 elements, that is, [1, 2, 3, …, 99]
  • xrange() returns a generator object. That means xrange does not create the list when it is called; instead, it returns an object that evaluates lazily.

Definition (Lazy evaluation): An object is said to be lazily evaluated if each element is only evaluated on demand rather than on initialization.

Let’s see that on code (remember to run this code on Python 2).

Output:

('range data type: ', <type 'list'>)
('xrange data type: ', <type 'xrange'>)
('range object 0 of 5 elements: ', [0, 1, 2, 3, 4])
('range object 0 of possible 5 elements: ', xrange(5))

From the output, it is apparent that the range() function returned a list and xrange() returned an xrange generator object.

In Python 3:

  • range() function is equivalent to Python 2’s xrange(). The function returns a generator object that can be converted into a list explicitly using list(range_object).
  • xrange() does not exist in Python 3.

Let’s run the following code in Python 3 to see the difference between range() and xrange().

Output:

range object in Python 3:  range(0, 10)
Type of range object in Python:  <class 'range'>
Cast range object into a list:  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
NameError: name 'xrange' is not defined

Clearly, the output shows that the range() function returns a generator that can be converted into a list using the list() function in Python 3. The result also indicates that xrange() is not available in Python 3.

Memory usage

The amount of memory used by the range() and xrange() functions depends on their return types. The function that creates a list in memory will take more space than a function that returns a generator object. That is to say,

  • Python 2: The xrange() generator object takes less space than the range() list.
  • Python 3: The range() generator takes less space than the list generated by list(range_object).

The amount of memory used up by Python 2’s range() and Python 3’s list(range_object) depends on the size of the list (the choice of start, stop, and step parameters in the function).

Using some code, let’s compare the memory usage for range() and xrange() in Python 2.

Output:

('size of range_obj0 of 5 elements', 112)
('size of xrange_obj0 generator of 5 elements', 40)
('size of range_obj1 of 500 elements', 4072)
('size of xrange_obj1 of 500  elements', 40)

From the output, notice that the generator object takes the same memory space (40 bytes) irrespective of “the size of the object.”

On the other hand, the range() object writes the elements of the returned list into memory, taking more space. The amount of space depends on the size of the list generated. In the example above, the 5-element list takes up 112 bytes, but the 500-element list is allotted 4072 bytes of memory.

The following code in Python 3 compares range() to list(range_object) based on memory usage.

Output:

Size of 5-element range object in Python3:  48
range object cast to list in Python 3:  [0, 1, 2, 3, 4]
Size of range object cast to list:  96
Size of 500-element range object in Python3:  48
Size of range object cast to list:  4056

Just like in Python 2, the range() object in Python 3 takes up the same space (48 bytes) irrespective of the “size of the range object.” On the other hand, if the range object is converted to a list, the result takes more space, and the amount of memory space taken depends on the size of the list.

Operations Usage

All operations applied to Python lists can also be used to range() in Python 2 and list(range_object) in Python 3.

This is because these two methods return Python lists. On the other hand, Python 2’s xrange() and Python 3’s range() may not support most of the list operations. Here are some examples.

Operations like append, insert, and remove fail when working with generators.

Speed

In Python 2, the evaluation of the xrange() object is faster than the range(). This is because the xrange object is only a generator required for lazy evaluation. Let’s run the following tests on the command line or terminal to ascertain that.

Output:

10 loops, best of 3: 23.5 msec per loop
python2.7 -m timeit 'for i in xrange(1000000):' ' pass'

Output:

10 loops, best of 3: 8.25 msec per loop

In Python 3, it is fast to loop on a range object than its equivalent list. Let’s make some runs to test that ascension.

Output:

10 loops, best of 5: 12.1 msec per loop
python3.9 -m timeit 'for i in list(range(1000000)):' ' pass'

Output:

10 loops, best of 5: 24.8 msec per loop

From the output, notice that working Python 2’s xrange is faster than Python 3’s equivalent range object.

Summary of the differences

The following table summarizes the differences we discussed between range() and xrange() functions.

Metrics range in Python 2 and Python 3 xrange in Python 2
Compatibility The function is available in Python 2 and Python 3. Only available in Python 2
Return types Returns a list in Python 2 and a generator object in Python 3. Return generator object.
Memory usage Python 2’s range takes more memory because it saves the returned list in memory. The amount of space taken depends on the size of the list. xrange takes a constant memory size of 40 bytes (a tiny memory space) irrespective of possible elements the object can generate.
Operations All list operations can be applied to range in Python 2 because it returns a list, but Python 3’s range may not support most list operations. Most list operations may not be applied to the xrange objects.
Speed range in Python 3 is faster than the range in Python 2 because the latter returns an actual list, but the former returns a generator that can be lazily evaluated. xrange has a faster evaluation than the range in Python 2 because xrange returns a generator, and range returns a list.