Должны ли вы всегда отдавать предпочтение xrange(), а не range()?

StackOverflow https://stackoverflow.com/questions/135041

  •  02-07-2019
  •  | 
  •  

Вопрос

Почему или почему нет?

Это было полезно?

Решение

Для повышения производительности, особенно при повторении в большом диапазоне, xrange() обычно лучше.Тем не менее, есть еще несколько случаев, почему вы можете предпочесть range():

  • В питоне 3 range() делает то, что xrange() раньше делал и xrange() не существует.Если вы хотите написать код, который будет работать как на Python 2, так и на Python 3, вы не можете использовать xrange().

  • range() в некоторых случаях может быть быстрее, например.если повторять одну и ту же последовательность несколько раз. xrange() каждый раз приходится восстанавливать целочисленный объект, но range() будут иметь реальные целочисленные объекты.(Однако он всегда будет работать хуже с точки зрения памяти)

  • xrange() не может использоваться во всех случаях, когда необходим реальный список.Например, он не поддерживает срезы или какие-либо методы списков.

[Изменить] Есть несколько сообщений, в которых упоминается, как range() будет обновлен с помощью инструмента 2to3.Для справки, вот результат запуска инструмента в некоторых примерах использования range() и 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)

Как вы можете видеть, при использовании в цикле for или в понимании, или там, где он уже обернут с помощью list(), диапазон остается неизменным.

Другие советы

Нет, они оба имеют свое применение:

Использовать xrange() при итерации, так как это экономит память.Сказать:

for x in xrange(1, one_zillion):

скорее, чем:

for x in range(1, one_zillion):

С другой стороны, используйте range() если вам действительно нужен список чисел.

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

Вы должны отдать предпочтение range() над xrange() только тогда, когда вам нужен актуальный список.Например, если вы хотите изменить список, возвращаемый range(), или когда вы хотите нарезать его.Для итерации или даже просто обычной индексации, xrange() будет работать нормально (и обычно гораздо эффективнее).Есть момент, где range() немного быстрее, чем xrange() для очень маленьких списков, но в зависимости от вашего оборудования и других деталей, безубыточность может быть при длине 1 или 2;не о чем беспокоиться.Предпочитать xrange().

Еще одно отличие заключается в том, что xrange() не может поддерживать числа, превышающие целые числа C, поэтому, если вы хотите иметь диапазон, используя встроенную поддержку больших чисел Python, вам придется использовать 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 не имеет этой проблемы:

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)

xrange() более эффективен, поскольку вместо создания списка объектов он генерирует по одному объекту за раз.Вместо 100 целых чисел, всех их издержек и списка для их размещения у вас есть только одно целое число за раз.Более быстрая генерация, лучшее использование памяти, более эффективный код.

Если мне специально не нужен список для чего-то, я всегда предпочитаю xrange()

range() возвращает список, xrange() возвращает объект xrange.

xrange() немного быстрее и немного эффективнее использует память.Но выигрыш не очень велик.

Дополнительная память, используемая списком, конечно, не просто тратится впустую, списки обладают большей функциональностью (разрез, повтор, вставка...).Точные различия можно найти в документация.Не существует жесткого правила: используйте то, что необходимо.

Python 3.0 все еще находится в разработке, но IIRC range() очень похож на xrange() из 2.X, а list(range()) можно использовать для создания списков.

Я просто хотел бы сказать, что ДЕЙСТВИТЕЛЬНО не так уж сложно получить объект xrange с функциями среза и индексирования.Я написал код, который работает чертовски хорошо и так же быстро, как xrange, когда это необходимо (итерации).

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)

Честно говоря, я думаю, что вся эта проблема довольно глупая, и xrange все равно должен все это делать...

Хороший пример приведен в книге: Практический Python Магнус Ли Хетланд

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

Я бы не рекомендовал использовать диапазон вместо xRange в предыдущем примере - хотя необходимы только первые пять чисел, диапазон вычисляет все числа, и это может занять много времени.С xrange это не проблема, поскольку он вычисляет только те числа, которые необходимы.

Да, я прочитал ответ @ Брайана:В Python 3 range() в любом случае является генератором, а xrange() не существует.

Используйте диапазон по следующим причинам:

1) xrange исчезнет в новых версиях Python.Это обеспечит вам легкую совместимость в будущем.

2) range возьмет на себя эффективность, связанную с xrange.

Хорошо, у всех здесь разные мнения относительно компромиссов и преимуществ xrange по сравнению с range.В основном они верны: xrange — это итератор, а range конкретизируется и создает фактический список.В большинстве случаев вы не заметите разницы между ними.(Вы можете использовать карту с диапазоном, но не с xrange, но она требует больше памяти.)

Однако я думаю, что вы хотите услышать, что предпочтительным выбором является xrange.Поскольку диапазон в Python 3 является итератором, инструмент преобразования кода 2to3 правильно преобразует все случаи использования xrange в диапазон и выдает ошибку или предупреждение при использовании диапазона.Если вы хотите быть уверенным, что ваш код будет легко конвертироваться в будущем, вы будете использовать только xrange и list(xrange), когда вы уверены, что вам нужен список.Я узнал об этом во время спринта CPython на PyCon в этом году (2008) в Чикаго.

  • range(): range(1, 10) возвращает список от 1 до 10 чисел и сохраняет весь список в памяти.
  • xrange():Нравиться range(), но вместо возврата списка возвращает объект, который по запросу генерирует числа в диапазоне.Для цикла это немного быстрее, чем range() и более эффективно использовать память. xrange() объект как итератор и генерирует числа по требованию (ленивая оценка).
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() делает то же самое, что и xrange() раньше делал в Python 3 и там нет термина xrange() существуют в Python 3.range() в некоторых сценариях на самом деле может быть быстрее, если вы повторяете одну и ту же последовательность несколько раз. xrange() каждый раз приходится восстанавливать целочисленный объект, но range() будут иметь реальные целочисленные объекты.

Пока xrange быстрее, чем range в большинстве случаев разница в производительности минимальна.Небольшая программа ниже сравнивает итерацию range и xrange:

import timeit
# Try various list sizes.
for list_len in [1, 10, 100, 1000, 10000, 100000, 1000000]:
  # Time doing a range and an xrange.
  rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n'%list_len, number=1000)
  xrtime = timeit.timeit('a=0;\nfor n in xrange(%d): a += n'%list_len, number=1000)
  # Print the result
  print "Loop list of len %d: range=%.4f, xrange=%.4f"%(list_len, rtime, xrtime)

Результаты ниже показывают, что xrange действительно быстрее, но недостаточно, чтобы потеть.

Loop list of len 1: range=0.0003, xrange=0.0003
Loop list of len 10: range=0.0013, xrange=0.0011
Loop list of len 100: range=0.0068, xrange=0.0034
Loop list of len 1000: range=0.0609, xrange=0.0438
Loop list of len 10000: range=0.5527, xrange=0.5266
Loop list of len 100000: range=10.1666, xrange=7.8481
Loop list of len 1000000: range=168.3425, xrange=155.8719

Так что во что бы то ни стало используйте xrange, но если вы не используете ограниченное оборудование, не беспокойтесь об этом слишком сильно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top