What is the difference between range and xrange in python?
I see that they produce the same result, is the difference in speed? Why are there 2 functions for the same ?
> range(5)
[0, 1, 2, 3, 4]
>>> xrange(5)
xrange(5)
>>> for i in range(5):
... print i
...
0
1
2
3
4
>>> for i in xrange(5):
... print i
...
0
1
2
3
4
3 answers
Yes, range
and xrange
they produce the same result but in different ways.
The bases
As you have already guessed the function range
returns a list:
>>> type(range(10))
list
The range function will occupy the amount of memory according to the size of the range you pass as a parameter.
On the other hand the function xrange
returns its own data type, the xrange-type :
>>> type(xrange(10))
xrange
For there is not much science behind the xrange
, in fact it has no difference from range
in terms of performance, the advantage is that xrange
will always occupy the same amount of memory (RAM) no matter the size of the range:
The xrange type is an immutable sequence which is commonly used for looping. The advantage of the xrange type is that an xrange object will always take the same amount of memory, no matter the size of the range it represents. There are no consistent performance advantages.
In short, the ultimate goal of both functions is to return lists but we could say that xrange
does so on demand because of its "lazy" or "lazy"nature.
Iterators
In both cases there is support for the iteration protocol since both have the method __iter__
:
>>> r = range(10)
>>> r
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> r.__iter__
<method-wrapper '__iter__' of list object at 0xb603fdcc>
>>> xr = xrange(10)
>>> xr
xrange(10)
>>> xr.__iter__
<method-wrapper '__iter__' of xrange object at 0xb600e4d0>
So the following cases are equivalent:
>>> for item in r:
... print item
...
0
1
2
3
4
5
6
7
8
9
>>> for item in r.__iter__():
... print item
...
0
1
2
3
4
5
6
7
8
9
The same alpica for xr
. The big difference is that when iterating on r
you are doing it on a list that has already been previously evaluated (and loaded into memory) and by iterating on xr
you are doing it on a "loose" list that is passing the values to you as long as you need them (loading into memory one at a time).
Now this may not make much sense with a range of 10 integers, but try it with a few million and you'll notice the difference in your RAM.
Generators
I know this is not part of the initial question but I found it pertinent to add it to the answer since generators are objects that ultimately implement the iteration protocol.
Are similar to comprehension lists but are created using parentheses instead of brackets.
Examples:
>>> lista = [1, 2, 3, 4, 5]
>>> [x**2 for x in lista]
[1, 4, 9, 16, 25]
>>> (x**2 for x in lista)
<generator object <genexpr> at 0xb60427fc>
Now, generators work similarly to xrange
since they are also "loose" and only return the value as long as you need it without loading everything into memory by using the expression yield
.
References
Function range
generates a list of temporary internal that is manipulated, article by article, while xrange
produces an interator (defines the interface to iterate through the addition of elements and access them, so that the client does not have to know the details and be able to deal with them anyway) through which you can pass without the expense of what could be an object of list big storm.
Example:
For x
in range(10000)
:
It will generate a list of 10 thousand elements and then it will go through each of them at once.
For x
in xrange(10000)
:
It will generate 10 thousand integers one by one, passing each to the variable x at a time.
In python2, they are different functions and it is explained quite well in the documentation of xrange their minimal differences: xrange
generates a xrange object, and range
generates a list. The advantage of the XRange
object is that it is not necessary to generate all the elements as long as it is not needed, which is a significant saving of resources. In addition, in the implementation CPython (which is the most common python) you can optimize much better and simple this type of loop.
With Python3 there are no longer two functions unifying into a single function range
which would be equivalent to the function xrange
of python2. To get a list you need to invoke the list constructor (eg: list(range(1000))
)