Python

Search for '{{search_term}}'

range vs. xrange in Python

CMOS is the Code-Maven Open Source podcast that also includes video interviews. Subscribe to this feed RSS feed with your Podcast listener app or via iTunes iTunes.

Python has a built-in function called range that can easily generate a a range of whole numbers. There is another built-in function call xrange that provides the same result, but uses a lot less memory.

range and xrange

In the following 3 example we could replace range by xrange and receive the same result:

examples/python/range_3_7.py

#!/usr/bin/env python
from __future__ import print_function

for v in range(3, 7):
    print(v)

From the first number (3) till one before the second number (7):

3
4
5
6

examples/python/range_5.py

#!/usr/bin/env python
from __future__ import print_function

for v in range(5):
    print(v)

If there is only one number, that will be the end number and the default start number will be 0. Thus range(5) is the same as range(0, 5)

0
1
2
3
4

examples/python/range_step.py

#!/usr/bin/env python
from __future__ import print_function

for v in range(0, 5, 2):
    print(v)

If there are 3 parameters, the first one is the 3 value becomes the "step" that defaults to 1 if the 3rd parameter is not present. The result will be the following:

0
2
4

Variable holding a range

What if we would like to create a variable that will hold the range? We can do that and it is quite simple with either range or xrange

r = range(1000)

Then we can go over the elements:

for v in r:
    pass

or we can access them by index:

print(r[4])

Memory Size

The big difference is in the amount of memory they use:

examples/python/range-memory.py

#!/usr/bin/env python
from __future__ import print_function
import sys

r = range(10000)
print(sys.getsizeof(r))  # 80072

x = xrange(10000)
print(sys.getsizeof(x))  # 40

The variable holding the range created by range uses 80072 bytes while the variable created by xrange only uses 40 bytes.

The reason is that range creates a list holding all the values while xrange creates an object that can iterate over the numbers on demand.

examples/python/range-type.py

#!/usr/bin/env python
from __future__ import print_function
import sys

r = range(10000)
x = xrange(10000)
print(type(r))    # <type 'list'>
print(type(x))    # <type 'xrange'>

Speed - Benchmarking range and xrange

The "cost" of the memory saving is that looking up indexes in xrange will take slightly longer. This benchmark code that uses the timeit module shows that the xrange version is 10% slower:

examples/python/range-benchmark.py

#!/usr/bin/env python
from __future__ import print_function
import timeit

r = range(10000)
x = xrange(10000)

def range_index():
    z = r[9999]

def range_xindex():
    z = x[9999]

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("range_index()", setup="from __main__ import range_index",   number=10000000))
    print(timeit.timeit("range_xindex()", setup="from __main__ import range_xindex", number=10000000))

The resulting numbers are:

2.16770005226
2.35304307938

Comments

In the comments, please wrap your code snippets within <pre> </pre> tags and use spaces for indentation.
comments powered by Disqus