Should you always favor xrange() over range()?

226

Solution 1

For performance, especially when you're iterating over a large range, xrange() is usually better. However, there are still a few cases why you might prefer range():

  • In python 3, range() does what xrange() used to do and xrange() does not exist. If you want to write code that will run on both Python 2 and Python 3, you can't use xrange().

  • range() can actually be faster in some cases - eg. if iterating over the same sequence multiple times. xrange() has to reconstruct the integer object every time, but range() will have real integer objects. (It will always perform worse in terms of memory however)

  • xrange() isn't usable in all cases where a real list is needed. For instance, it doesn't support slices, or any list methods.

[Edit] There are a couple of posts mentioning how range() will be upgraded by the 2to3 tool. For the record, here's the output of running the tool on some sample usages of range() and xrange()

RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: ws_comma
--- range_test.py (original)
+++ range_test.py (refactored)
@@ -1,7 +1,7 @@

 for x in range(20):
-    a=range(20)
+    a=list(range(20))
     b=list(range(20))
     c=[x for x in range(20)]
     d=(x for x in range(20))
-    e=xrange(20)
+    e=range(20)

As you can see, when used in a for loop or comprehension, or where already wrapped with list(), range is left unchanged.

Solution 2

No, they both have their uses:

Use xrange() when iterating, as it saves memory. Say:

for x in xrange(1, one_zillion):

rather than:

for x in range(1, one_zillion):

On the other hand, use range() if you actually want a list of numbers.

multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven

Solution 3

You should favour range() over xrange() only when you need an actual list. For instance, when you want to modify the list returned by range(), or when you wish to slice it. For iteration or even just normal indexing, xrange() will work fine (and usually much more efficiently). There is a point where range() is a bit faster than xrange() for very small lists, but depending on your hardware and various other details, the break-even can be at a result of length 1 or 2; not something to worry about. Prefer xrange().

Solution 4

One other difference is that Python 2 implementation of xrange() can't support numbers bigger than C ints, so if you want to have a range using Python's built in large number support, you have to use range().

Python 2.7.3 (default, Jul 13 2012, 22:29:01) 
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
[123456787676676767676676L, 123456787676676767676677L, 123456787676676767676678L]
>>> xrange(123456787676676767676676,123456787676676767676679)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C long

Python 3 does not have this problem:

Python 3.2.3 (default, Jul 14 2012, 01:01:48) 
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
range(123456787676676767676676, 123456787676676767676679)

Solution 5

xrange() is more efficient because instead of generating a list of objects, it just generates one object at a time. Instead of 100 integers, and all of their overhead, and the list to put them in, you just have one integer at a time. Faster generation, better memory use, more efficient code.

Unless I specifically need a list for something, I always favor xrange()

Share:
226
namu
Author by

namu

Updated on June 30, 2021

Comments

  • namu
    namu almost 3 years

    In main.C I am trying to access a nested structure member (g.Fp1.d_status) and am getting the error:

    'g.GridPt::Fp1' does not have class type

    Below is my code. I can realy use some help. Thanks. I tried stuff like g.MarchingDirection::Fp1.d_status, g.&MarchingDirection::Fp1.d_status, ... with no luck.


    //Filename: main.C
    
    #include "GridPt.h"
    
    int main()
    {   
        GridPt g;
    
        g.d_lstDistFn;
        g.Fp1.d_status; \\ERROR: 'g.GridPt::Fp1' does not have class type
    
        return 0;
    }
    

    //Filename: MarchingDirection.h
    
    #ifndef included_MarchingDirection
    #define included_MarchingDirection
    
    struct MarchingDirection
    {
        MarchingDirection(double FValue);   
    
        double d_F;              
        int d_inOut;             
        char d_status;          
    };
    #endif
    

    //Filename: MarchingDirection.C
    
    #include "MarchingDirection.h"
    
    MarchingDirection::MarchingDirection(double FValue)  
    : d_F(FValue),
      d_inOut(static_cast<int>(FValue)),
      d_status('d'){}
    

    //Filename: Grid2D.h
    
    #ifndef included_GridPt
    #define included_GridPt
    
    #include "MarchingDirection.h"
    #include <cstddef>
    
    struct GridPt
    {   
        GridPt();    
    
        double d_x, d_y; 
        MarchingDirection Fp1(double F=1.0), Fm1(double F=-1.0);
        double d_lstDistFn;    
    };
    
    #endif 
    

    //Filename: GridPt.C
    
    #include "GridPt.h"
    
    GridPt::GridPt() 
    : d_lstDistFn(0.0){}
    
  • namu
    namu over 11 years
    OK, I got it! Then I use <pre><code> GridPt::GridPt() //constructor for initializing Fast Marching Method : Fp1(1.0), Fm1(-1.0), d_lstDistFn(0.0) { }//GridPt constructor</code></pre>