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

后端 未结 12 2012
后悔当初
后悔当初 2020-11-22 13:36

Why or why not?

相关标签:
12条回答
  • 2020-11-22 14:06

    Go with range for these reasons:

    1) xrange will be going away in newer Python versions. This gives you easy future compatibility.

    2) range will take on the efficiencies associated with xrange.

    0 讨论(0)
  • 2020-11-22 14:08

    One other difference is that 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)
    
    0 讨论(0)
  • 2020-11-22 14:10

    I would just like to say that it REALLY isn't that difficult to get an xrange object with slice and indexing functionality. I have written some code that works pretty dang well and is just as fast as xrange for when it counts (iterations).

    from __future__ import division
    
    def read_xrange(xrange_object):
        # returns the xrange object's start, stop, and step
        start = xrange_object[0]
        if len(xrange_object) > 1:
           step = xrange_object[1] - xrange_object[0]
        else:
            step = 1
        stop = xrange_object[-1] + step
        return start, stop, step
    
    class Xrange(object):
        ''' creates an xrange-like object that supports slicing and indexing.
        ex: a = Xrange(20)
        a.index(10)
        will work
    
        Also a[:5]
        will return another Xrange object with the specified attributes
    
        Also allows for the conversion from an existing xrange object
        '''
        def __init__(self, *inputs):
            # allow inputs of xrange objects
            if len(inputs) == 1:
                test, = inputs
                if type(test) == xrange:
                    self.xrange = test
                    self.start, self.stop, self.step = read_xrange(test)
                    return
    
            # or create one from start, stop, step
            self.start, self.step = 0, None
            if len(inputs) == 1:
                self.stop, = inputs
            elif len(inputs) == 2:
                self.start, self.stop = inputs
            elif len(inputs) == 3:
                self.start, self.stop, self.step = inputs
            else:
                raise ValueError(inputs)
    
            self.xrange = xrange(self.start, self.stop, self.step)
    
        def __iter__(self):
            return iter(self.xrange)
    
        def __getitem__(self, item):
            if type(item) is int:
                if item < 0:
                    item += len(self)
    
                return self.xrange[item]
    
            if type(item) is slice:
                # get the indexes, and then convert to the number
                start, stop, step = item.start, item.stop, item.step
                start = start if start != None else 0 # convert start = None to start = 0
                if start < 0:
                    start += start
                start = self[start]
                if start < 0: raise IndexError(item)
                step = (self.step if self.step != None else 1) * (step if step != None else 1)
                stop = stop if stop is not None else self.xrange[-1]
                if stop < 0:
                    stop += stop
    
                stop = self[stop]
                stop = stop
    
                if stop > self.stop:
                    raise IndexError
                if start < self.start:
                    raise IndexError
                return Xrange(start, stop, step)
    
        def index(self, value):
            error = ValueError('object.index({0}): {0} not in object'.format(value))
            index = (value - self.start)/self.step
            if index % 1 != 0:
                raise error
            index = int(index)
    
    
            try:
                self.xrange[index]
            except (IndexError, TypeError):
                raise error
            return index
    
        def __len__(self):
            return len(self.xrange)
    

    Honestly, I think the whole issue is kind of silly and xrange should do all of this anyway...

    0 讨论(0)
  • 2020-11-22 14:13

    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
    
    0 讨论(0)
  • 2020-11-22 14:15

    A good example given in book: Practical Python By Magnus Lie Hetland

    >>> zip(range(5), xrange(100000000))
    [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
    

    I wouldn’t recommend using range instead of xrange in the preceding example—although only the first five numbers are needed, range calculates all the numbers, and that may take a lot of time. With xrange, this isn’t a problem because it calculates only those numbers needed.

    Yes I read @Brian's answer: In python 3, range() is a generator anyway and xrange() does not exist.

    0 讨论(0)
  • 2020-11-22 14:22
    • range(): range(1, 10) returns a list from 1 to 10 numbers & hold whole list in memory.
    • xrange(): Like range(), but instead of returning a list, returns an object that generates the numbers in the range on demand. For looping, this is lightly faster than range() and more memory efficient. xrange() object like an iterator and generates the numbers on demand (Lazy Evaluation).
    In [1]: range(1,10)
    Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    In [2]: xrange(10)
    Out[2]: xrange(10)
    
    In [3]: print xrange.__doc__
    Out[3]: xrange([start,] stop[, step]) -> xrange object
    

    range() does the same thing as xrange() used to do in Python 3 and there is not term xrange() exist in Python 3. range() can actually be faster in some scenario if you iterating over the same sequence multiple times. xrange() has to reconstruct the integer object every time, but range() will have real integer objects.

    0 讨论(0)
提交回复
热议问题