What is the difference between range and xrange functions in Python 2.X?

443,650

Solution 1

In Python 2.x:

  • range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements.

  • xrange is a sequence object that evaluates lazily.

In Python 3:

  • range does the equivalent of Python 2's xrange. To get the list, you have to explicitly use list(range(...)).
  • xrange no longer exists.

Solution 2

range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements.

xrange is a generator, so it is a sequence object is a that evaluates lazily.

This is true, but in Python 3, range() will be implemented by the Python 2 xrange(). If you need to actually generate the list, you will need to do:

list(range(1,100))

Solution 3

Remember, use the timeit module to test which of small snippets of code is faster!

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

Personally, I always use range(), unless I were dealing with really huge lists -- as you can see, time-wise, for a list of a million entries, the extra overhead is only 0.04 seconds. And as Corey points out, in Python 3.0 xrange() will go away and range() will give you nice iterator behavior anyway.

Solution 4

xrange only stores the range params and generates the numbers on demand. However the C implementation of Python currently restricts its args to C longs:

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

Note that in Python 3.0 there is only range and it behaves like the 2.x xrange but without the limitations on minimum and maximum end points.

Solution 5

xrange returns an iterator and only keeps one number in memory at a time. range keeps the entire list of numbers in memory.

Share:
443,650
Teifion
Author by

Teifion

I am a Software Engineer in a UK Car Insurance provider. I mainly work in Python and PHP.

Updated on May 18, 2021

Comments

  • Teifion
    Teifion about 3 years

    Apparently xrange is faster but I have no idea why it's faster (and no proof besides the anecdotal so far that it is faster) or what besides that is different about

    for i in range(0, 20):
    for i in xrange(0, 20):
    
  • Teifion
    Teifion over 15 years
    The library is good but it's not always so easy to get the answer to the question you have.
  • Simon Dugré
    Simon Dugré over 15 years
    Go to the library reference, hit ctrl+f, search for range and you will get two results. It's not much effort to find the answer to this question.
  • Benjamin Autin
    Benjamin Autin over 15 years
    I don't see that being a huge problem (regarding breaking existing applications) as range was mostly for generating indexes to be used in for loops as "for i in range(1, 10):"
  • Cervo
    Cervo about 12 years
    +1 Thanks for this answer, the information about Python 3 replacing range with xrange is very useful. I actually told someone to use xrange instead or range and they said that it did not matter in python 3, so I google searched for more information and this answer came up :)
  • stalk
    stalk almost 12 years
    +1 for timeit example. Note: to run in windows cmd it is needed to use double quote, i.e. ". So code will be python -m timeit "for i in xrange(1000000):" " pass"
  • Vajk Hermecz
    Vajk Hermecz over 11 years
    Running a benchmark like this, one time, doesnt provide exact timing results. There is always a variance.. It could be either GC, or another process stealing the CPU... anything. That's why benchmarks are usually run 10-100-1000-...
  • Dave Everitt
    Dave Everitt over 11 years
    this is just a hasty snippet printout - I ran it a few times, but only up to around 100, and xrange seemed slightly quicker, although with Python 3 the comparison is now redundant.
  • endolith
    endolith almost 10 years
    The main benefit of xrange is memory, not time.
  • Bob Stein
    Bob Stein almost 10 years
    +1 for the practical answer: use range unless huge. BTW they are conceptually identical, correct? Oddly no answer spells that out.
  • João dos Reis
    João dos Reis over 9 years
    Why did they make xrange, rather than making range lazy?
  • Austin Mohr
    Austin Mohr over 9 years
    If xrange is faster and doesn't hog memory, why ever use range?
  • chacham15
    chacham15 over 9 years
    I agree with your statement generally, but your evaluation is wrong: the extra overhead is only 0.04 seconds isnt the correct way to look at it, (90.5-51.1)/51.1 = 1.771 times slower is correct because it conveys that if this is the core loop of your program it can potentially bottleneck it. However, if this is a small part then 1.77x isnt much.
  • Alvaro
    Alvaro about 9 years
    @RobertGrant If you iterate over that list 1000 times, it'll be slower to generate the values each time
  • abarnert
    abarnert about 9 years
    xrange does not return an iterator.
  • abarnert
    abarnert about 9 years
    xrange does not return a generator object.
  • abarnert
    abarnert about 9 years
    I realize this is 5 years old, but that post is wrong about nearly everything. xrange is not an iterator. The list returned by range does support iteration (a list is pretty much the prototypical example of an iterable). The overall benefit of xrange is not "minimal". And so on.
  • abarnert
    abarnert about 9 years
    This is what timeit is for. It takes care of running many times, disabling GC, using the best clock instead of time, etc.
  • kmario23
    kmario23 about 9 years
    If I understand correctly, that is how it is explained here(for Python 2.x): wiki.python.org/moin/Generators
  • abarnert
    abarnert about 9 years
    Then the wiki is wrong. (I don't know who the "SH" is who added and signed that comment.) The official documentation is right; you can test it yourself and see whether it's a generator or a sequence.
  • kmario23
    kmario23 about 9 years
    ok. But it's still confusing after reading this: stackoverflow.com/questions/135041/…
  • abarnert
    abarnert about 9 years
    If you read even just the first sentence of that comment, it says "xrange(1000) is an object that acts like a generator (although it certainly is not one)." (emphasis mine) But even if it didn't say that, are you going to trust a throwaway comment by a random SO user over the official Python documentation, or over what you can see by testing it yourself?
  • abarnert
    abarnert about 9 years
    At any rate, it really doesn't act like a generator at all. A sequence is a thing that can be iterated repeatedly, because its __iter__ returns a new object, and it can be indexed, because it has a __getitem__, and so on. A generator is a thing that's consumed as its iterated, because its __iter__ returns self, and it can't be indexed, and it contains a suspended stack frame, and it has send and throw methods. Which one of those does xrange sound like?
  • abarnert
    abarnert about 9 years
    The fun question is what to do when the interpreter disagrees with the official docs, or with a different interpreter… But fortunately, that doesn't come up too often…
  • Paul Draper
    Paul Draper about 9 years
    @RobertGrant, they did. In Python 3. (They couldn't do that in the Python 2.x line, since all changes must be backwards compatible.)
  • abarnert
    abarnert almost 9 years
    xrange does not create an iterator instance. It creates an xrange object, which is iterable, but not an iterator—almost (but not quite) a sequence, like a list.
  • ratulotron
    ratulotron almost 9 years
    Can someone explain what "evaluates lazily" means? Thanks!
  • Onilol
    Onilol over 8 years
    @Ratul it means that each i is evaluated on demand rather than on initialization.
  • th3an0maly
    th3an0maly about 8 years
    xrange() isn't a generator. xrange(n).__iter__()` is.
  • Darshan Chaudhary
    Darshan Chaudhary about 8 years
    xrange is an iterator that evaluates lazily? Calling the iter() method on xrange returns a generator?
  • TM.
    TM. about 8 years
    This is bad advice. Really the only upside to range is if you have some aesthetic opinion on it being less ugly to read than xrange, or if you actually need a list. Additionally, running timeit on your machine is likely not going to resemble the environments in which your code is going to be running, and does nothing to tell you about the memory implications.
  • SIslam
    SIslam over 7 years
    and only keeps one number in memory at a time and where the rest are placed please guide me..
  • Justin Meiners
    Justin Meiners over 7 years
    @SIslam If it knows the start, end, and current, it can compute the next, one at a time.
  • Sandeep
    Sandeep almost 6 years
    The library reference is not working. Can you please update it?
  • winterlight
    winterlight about 5 years
    What is wrong with calling xrange a generator? It is a function containing yield statement, and according to glossary such functions are called generators.
  • McSinyx
    McSinyx about 4 years
    @winterlight, think the correct term for it is iterator. Generators should be able to receive as well.
  • scign
    scign almost 4 years
    @McSinyx - winterlight is correct that a function that returns a generator is also referred to as a generator. A generator object is a special case of iterator. See generators vs iterators.
  • McSinyx
    McSinyx almost 4 years
    @scign, see PEP 342 for the canonical definition of the generator protocol. A nice sum up can be found in type annotation documentation (these are aliased as typing.*).
  • scign
    scign almost 4 years
    @McSinyx glad you agree.
  • McSinyx
    McSinyx almost 4 years
    No I don't, @scign. Have you read the linked PEP and doc? In the past the two terms might have been used interchangeably, but at the time of writing, generators must be able to receive values. In addition, Python 3 range is not an iterator either (try next(range(42))).
  • ShadowRanger
    ShadowRanger over 3 years
    @DarshanChaudhary: xrange is an iterable (with sequence-like behaviors) that evaluates lazily, not an iterator. Calling iter() on it creates an iterator (which people loosely call a generator, though in Python, "generator" is a specific type of iterator made with a generator expression or a function using the yield keyword, neither of which actually applies to xrange).
  • Daniel
    Daniel almost 2 years
    To use xrange in py3 and have py2 backwards compatibility use: from past.builtins import xrange