1

I came across various post and study material for xrange and Python generators, but its been quite long that I never saw someone comparing these 2 things together.

Its clear that xrange is not accepted in python3+, but can we consider generators as upgraded version of Python 2 xrange?

Both uses a iterator objects, both uses next() call. But the advantage of generator I learnt, we could make it pause with "yield", which is no such such thing for xrange.

Please help me understand the concept of both, and in which sense they are different? Also why xrange had to deprecated?

Ankzious
  • 311
  • 3
  • 18
  • 5
    Python 3's `range` is the upgraded version of Python 2's `xrange`. Generators existed back in Python 2; they're quite orthogonal. I don't know why `yield` makes any sense in the context of a range object; if you need a generator, you should be using a generator; if you need a range object, you should be using a range object. – Charles Duffy Dec 09 '20 at 17:43
  • 2
    In Python 3, `range` is the equivalent of `xrange` (although it has various impovements). `range` objects are not really like generator objects at all. `range` objects are specialized *containers* of `int` objects that can be represented by a `strop`, `start`, and `step`. Generators are iterators. `range` objects are not iterators. `range` objects are sequences, generators are not sequences. – juanpa.arrivillaga Dec 09 '20 at 17:47
  • So what is the cause of xrange deadend? Are they too hideous to carry on with some upgrades to 3+. Sorry for dumb questions, I never came across use of xrange much. – Ankzious Dec 09 '20 at 17:50
  • 1
    It's not dead, it's been implemented as the base range function – TheLazyScripter Dec 09 '20 at 17:51
  • @AnkurParanjpe it isn't dead, the name was changed to `range` and the old, python 2 `range` functionality was removed. In Python 3, you would need to do `list(range(x))` for the equivalent behavior of Python 2 `range(x)` – juanpa.arrivillaga Dec 09 '20 at 18:26

2 Answers2

2

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.

chepner
  • 497,756
  • 71
  • 530
  • 681
1

Both uses a iterator objects, both uses next() call. But the advantage of generator I learnt, we could make it pause with "yield", which is no such such thing for xrange.

xrange does pause between iterations. Compare these 2 lines of Python 2 code:

>>> a = range(150000000)   # takes about 3 seconds and 5 GB on my machine
>>> b = xrange(150000000)   # instant on my machine

range actually goes ahead and build up the entire list in memory. You can verify this by seeing your system RAM usage go up when you enter it.

What xrange does is actually pausing/yielding between iterations. If we iterate over b in a for loop, it will just produce the first element (0) in its first iteration, then its second element (1) in its second, and so on.

Here is a way one could implement xrange as a generator:

def my_range(stop):
    start = 0
    while start < stop:
        yield start
        start += 1
>>> c = my_range(150000000)

Two notes here:

xjcl
  • 12,848
  • 6
  • 67
  • 89