Why is there no xrange function in Python3?
Question: Recently I started using Python3 and it's lack of xrange hurts.

Simple example:

**1)** Python2:

    
    
    from time import time as t
    def count():
      st = t()
      [x for x in xrange(10000000) if x%4 == 0]
      et = t()
      print et-st
    count()
    

**2)** Python3:

    
    
    from time import time as t
    
    def xrange(x):
    
        return iter(range(x))
    
    def count():
        st = t()
        [x for x in xrange(10000000) if x%4 == 0]
        et = t()
        print (et-st)
    count()
    

The results are, respectively:

**1)** 1.53888392448 **2)** 3.215819835662842

Why is that? I mean, why xrange's been removed? It's such a great tool to
learn. For the beginners, just like myself, like we all were at some point.
Why remove it? Can somebody point me to the proper PEP, I can't find it.

Cheers.


Answer: Some performance measurements, using `timeit` instead of trying to do it
manually with `time`.

First, Apple 2.7.2 64-bit:

    
    
    In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
    1 loops, best of 3: 1.05 s per loop
    

Now, python.org 3.3.0 64-bit:

    
    
    In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
    1 loops, best of 3: 1.32 s per loop
    
    In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
    1 loops, best of 3: 1.31 s per loop
    
    In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0) 
    1 loops, best of 3: 1.33 s per loop
    

Apparently, 3.x `range` really is a bit slower than 2.x `xrange`. And the OP's
`xrange` function has nothing to do with it. (Not surprising, as a one-time
call to the `__iter__` slot isn't likely to be visible among 10000000 calls to
whatever happens in the loop, but someone brought it up as a possibility.)

But it's only 30% slower. How did the OP get 2x as slow? Well, if I repeat the
same tests with 32-bit Python, I get 1.58 vs. 3.12. So my guess is that this
is yet another of those cases where 3.x has been optimized for 64-bit
performance in ways that hurt 32-bit.

But does it really matter? Check this out, with 3.3.0 64-bit again:

    
    
    In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
    1 loops, best of 3: 3.65 s per loop
    

So, building the `list` takes more than twice as long than the entire
iteration.

And as for "consumes much more resources than Python 2.6+", from my tests, it
looks like a 3.x `range` is exactly the same size as a 2.x `xrange`—and, even
if it were 10x as big, building the unnecessary list is still about 10000000x
more of a problem than anything the range iteration could possibly do.

And what about an explicit `for` loop instead of the C loop inside `deque`?

    
    
    In [87]: def consume(x):
       ....:     for i in x:
       ....:         pass
    In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
    1 loops, best of 3: 1.85 s per loop
    

So, almost as much time wasted in the `for` statement as in the actual work of
iterating the `range`.

If you're worried about optimizing the iteration of a range object, you're
probably looking in the wrong place.

* * *

Meanwhile, you keep asking why `xrange` was removed, no matter how many times
people tell you the same thing, but I'll repeat it again: It was not removed:
it was renamed to `range`, and the 2.x `range` is what was removed.

Here's some proof that the 3.3 `range` object is a direct descendant of the
2.x `xrange` object (and not of the 2.x `range` function): the source to [3.3
`range`](http://hg.python.org/cpython/file/3.3/Objects/rangeobject.c) and [2.7
`xrange`](http://hg.python.org/cpython/file/2.7/Objects/rangeobject.c). You
can even see the [change
history](http://hg.python.org/cpython/diff/a6eb6acfe04a/Objects/rangeobject.c)
(linked to, I believe, the change that replaced the last instance of the
string "xrange" anywhere in the file).

So, why is it slower?

Well, for one, they've added a lot of new features. For another, they've done
all kinds of changes all over the place (especially inside iteration) that
have minor side effects. And there'd been a lot of work to dramatically
optimize various important cases, even if it sometimes slightly pessimizes
less important cases. Add this all up, and I'm not surprised that iterating a
`range` as fast as possible is now a bit slower. It's one of those less-
important cases that nobody would ever care enough to focus on. No one is
likely to ever have a real-life use case where this performance difference is
the hotspot in their code.

