Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is xrange able to go back to beginning in Python?

I've encountered this code from Most pythonic way of counting matching elements in something iterable

r = xrange(1, 10) print sum(1 for v in r if v % 2 == 0) # 4 print sum(1 for v in r if v % 3 == 0) # 3 

r is iterated once. and then it's iterated again. I thought if an iterator is once consumed then it's over and it should not be iterated again.

Generator expressions can be iterated only once:

r = (7 * i for i in xrange(1, 10)) print sum(1 for v in r if v % 2 == 0) # 4 print sum(1 for v in r if v % 3 == 0) # 0 

enumerate(L) too:

r = enumerate(mylist) 

and file object too:

f = open(myfilename, 'r') 

Why does xrange behave differently?

like image 782
Le Curious Avatar asked May 27 '12 18:05

Le Curious


2 Answers

Because xrange does not return a generator. It returns an xrange object.

>>> type(xrange(10)) <type 'xrange'> 

In addition to repeated iteration, xrange objects support other things that generators don't -- like indexing:

>>> xrange(10)[5] 5 

They also have a length:

>>> len(xrange(10)) 10 

And they can be reversed:

>>> list(reversed(xrange(10))) [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 

In short, xrange objects implement the full sequence interface:

>>> import collections >>> isinstance(xrange(10), collections.Sequence) True 

They just do it without using up a lot of memory.

Note also that in Python 3, the range object returned by range has all the same properties.

like image 123
senderle Avatar answered Sep 21 '22 14:09

senderle


Because the xrange object produced by calling xrange() specifies an __iter__ that provides a unique version of itself (actually, a separate rangeiterator object) each time it's iterated.

>>> x = xrange(3) >>> type(x) <type 'xrange'> >>> i = x.__iter__() >>> type(i) <type 'rangeiterator'> 
like image 41
Amber Avatar answered Sep 20 '22 14:09

Amber