Difference Between Range and Xrange Functions in Python 2.X

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

In Python 2.x:

  • range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements.

  • xrange is a sequence object that evaluates lazily.

In Python 3:

  • range does the equivalent of Python 2's xrange. To get the list, you have to explicitly use list(range(...)).
  • xrange no longer exists.

Python 3 range Vs Python 2 range

Python 3 uses iterators for a lot of things where python 2 used lists.The docs give a detailed explanation including the change to range.

The advantage is that Python 3 doesn't need to allocate the memory if you're using a large range iterator or mapping.
For example

for i in range(1000000000): print(i)

requires a lot less memory in python 3.
If you do happen to want Python to expand out the list all at once you can

list_of_range = list(range(10))

Should you always favor xrange() over range()?

For performance, especially when you're iterating over a large range, xrange() is usually better. However, there are still a few cases why you might prefer range():

  • In python 3, range() does what xrange() used to do and xrange() does not exist. If you want to write code that will run on both Python 2 and Python 3, you can't use xrange().

  • range() can actually be faster in some cases - eg. if iterating over the same sequence multiple times. xrange() has to reconstruct the integer object every time, but range() will have real integer objects. (It will always perform worse in terms of memory however)

  • xrange() isn't usable in all cases where a real list is needed. For instance, it doesn't support slices, or any list methods.

[Edit] There are a couple of posts mentioning how range() will be upgraded by the 2to3 tool. For the record, here's the output of running the tool on some sample usages of range() and xrange()

RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: ws_comma
--- range_test.py (original)
+++ range_test.py (refactored)
@@ -1,7 +1,7 @@

for x in range(20):
- a=range(20)
+ a=list(range(20))
b=list(range(20))
c=[x for x in range(20)]
d=(x for x in range(20))
- e=xrange(20)
+ e=range(20)

As you can see, when used in a for loop or comprehension, or where already wrapped with list(), range is left unchanged.

Difference between Python xrange and generators?

Both range and generator objects are examples of iterable objects.

A range is defined by a stopping value, with an optional starting value and optional step size allowed. The result is an iterable of integers. A range also supports operations (like containment) that are not necessarily supported by other iterables.

A generator is defined by a generator expression or a generator function, either of which results in an iterable of arbitrary values.

The iterable aspect of a range object can be simulated by a generator:

def myrange(stop, start=None, step=None):
if start is not None:
from_ = stop
to_ = start
else:
from_ = 0
to_ = stop

if step is None:
step = 1 if from_ < to_ else -1

while from_ < to_:
yield from_
from_ += step

Then

>>> list(myrange(1, 10))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(1, 10))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

As Python 2 reached end-of-life nearly a year ago, there's not much reason to go into range. Suffice it to say, in Python 2 range was a function that returned a list of integers, while xrange was a type whose value represents a list of integers. Python 3 did away with the function and reused the name for the type.

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

In Python 2.x:

  • range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements.

  • xrange is a sequence object that evaluates lazily.

In Python 3:

  • range does the equivalent of Python 2's xrange. To get the list, you have to explicitly use list(range(...)).
  • xrange no longer exists.

How is irange() any different from range() or xrange()?

irange() returns a generator type, which can only be iterated over. Nothing else. Once you iterated over it, the generator is exhausted and can not be iterated over again.

The Python 2 xrange() type and Python 3 range() type are sequence types, they support various operations that other sequences support as well, such as reporting on their length, test for containment, and indexing:

>>> xr = xrange(10, 20, 3)
>>> len(xr)
4
>>> 10 in xr
True
>>> xr[0]
10
>>> xr[1]
13

You can iterate over these objects more than once:

>>> for i in xr:
... print i,
...
10 13 16 19
>>> for i in xr:
... print i,
...
10 13 16 19

You can even use the reversed() function to iterate over them in reverse, efficiently:

>>> for i in reversed(xr):
... print i,
...
19 16 13 10

The Python 3 range() type is an improved version of xrange(), in that it supports more sequence operations, is more efficient still, and can handle values beyond sys.maxint (what would be a long integer in Python 2).

It supports slicing, for example, which results in a new range() object for the sliced values:

>>> r = range(10, 20, 3)
>>> r[:2]
range(10, 16, 3)

You can use negative indices just like you can with other Python sequences, to get elements counting from the end:

>>> r[-2]
16
>>> r[-2:]
range(16, 22, 3)

and the type supports testing for equality; two range() instances are equal if they'd yield the same values:

>>> range(10, 20, 3) == range(10, 21, 3)
True

In Python 2, the only advantage the generator irange() might have is that it doesn't suffer from the limitation to non-long integers that xrange() is subjected to:

>>> import sys
>>> xrange(sys.maxint)
xrange(9223372036854775807)
>>> xrange(sys.maxint + 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C long

range and xrange for 13-digit numbers in Python?

You could try this. Same semantics as range:

import operator
def lrange(num1, num2 = None, step = 1):
op = operator.__lt__

if num2 is None:
num1, num2 = 0, num1
if num2 < num1:
if step > 0:
num1 = num2
op = operator.__gt__
elif step < 0:
num1 = num2

while op(num1, num2):
yield num1
num1 += step

>>> list(lrange(138264128374162347812634134, 138264128374162347812634140))
[138264128374162347812634134L, 138264128374162347812634135L, 138264128374162347812634136L, 138264128374162347812634137L, 138264128374162347812634138L, 138264128374162347812634139L]

Another solution would be using itertools.islice, as suggested inxrange's documentation



Related Topics



Leave a reply



Submit