2

The snippets

 xi = xrange(10)
 zip(xi,xi)

and

 xi = iter(range(10))
 zip(xi,xi)

behave differently. I expected to get

  [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]

in the first snippet as well, but it returns

[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9)]

instead. It seems the implicit container is being silently copied. Could anyone explain whats going on here ? and reasoning behind choosing such semantics.

>>> sys.version
'2.7.9 (default, Dec 10 2014, 12:28:03) [MSC v.1500 64 bit (AMD64)]'
srean
  • 2,578
  • 18
  • 18
  • "It seems the implicit container is being silently copied." - what do you mean? Why would a copy be necessary to produce the observed behavior? What would you have expected from `xi = [1, 2, 3, 4]; zip(xi, xi)`? – user2357112 Jun 26 '15 at 07:56
  • (Also, despite people calling it a generator over and over, xrange isn't a generator. An xrange is an immutable sequence.) – user2357112 Jun 26 '15 at 07:57
  • @user2357112 Your second comment explains it. I expected xrange to be "stateful" as a generator, and not immutable. – srean Jun 26 '15 at 07:59

3 Answers3

1

The xrange object is not already an iterator, but an iterable object. You get the iterator by feeding it to the iter function. The zip function implicitly calls iter on all of its arguments, so it produces two parallel iterators over the xrange object. In the second example, you call iter once by hand, so you are comparing apples to oranges. To get the effect you want also for xrange, you should do

In [5]: it = iter(xrange(10))
   ...: zip(it, it)
Out[5]: [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
Bas Swinckels
  • 18,095
  • 3
  • 45
  • 62
1

An xrange is not any sort of iterator. People keep calling it a generator, but it's not; an xrange is an immutable sequence, like a tuple:

>>> x = xrange(5)
>>> x[2]
2
>>> for i in x:
...     print i
...
0
1
2
3
4
>>> for i in x:
...     print i
...
0
1
2
3
4

As with any other sequence type, each time you request an iterator from an xrange, you get a new, independent iterator. Thus, when you zip xrange(10) with itself, you get the same output as if you had zipped [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] with itself, rather than if you had zipped iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) with itself.

user2357112
  • 260,549
  • 28
  • 431
  • 505
1

I think you have a miss understanding about xrange types. based on pythons documentation about xrange type :

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.

And following important part :

XRange objects have very little behavior: they only support indexing, iteration, and the len() function.

The xrange object is not an iterator its just an opaque sequence type which yields the same values as the corresponding list, without actually storing them all simultaneously,Since you can not access to an iterator elements without traversing its preceding items and it doesn't supports operations like indexing or len() function.and when you traverse the iterator you can not go back!!

So in the second code the zip function consume each item at each iteration and it just can access to the next item.

Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • Indeed. I always thought xrange had the same semantics as a generator. Happy to stand corrected. – srean Jun 26 '15 at 08:24
  • @srean Actually its approximately 90% like `range` but for example for an iterator we can say its 10% like a range :) (just for example) – Mazdak Jun 26 '15 at 08:26