Следует ли всегда отдавать предпочтение xrange () перед range ()?

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


person mbac32768    schedule 25.09.2008    source источник
comment
Может кто-нибудь вкратце описать разницу между двумя для нас, ребят, не питоновцев? Может быть, что-то вроде xrange () делает все, что делает range (), но также поддерживает X, Y и Z   -  person Outlaw Programmer    schedule 26.09.2008
comment
range (n) создает список, содержащий все целые числа 0..n-1. Это проблема, если вы используете диапазон (1000000), потому что в итоге вы получите список ›4 Мб. xrange справляется с этим, возвращая объект, который притворяется списком, но просто вычисляет необходимое число из запрошенного индекса и возвращает его.   -  person Brian    schedule 26.09.2008
comment
См. stackoverflow.com/questions/94935   -  person James McMahon    schedule 13.04.2012
comment
По сути, тогда как range(1000) - это list, xrange(1000) - это объект, который действует как generator (хотя, конечно, не). Кроме того, xrange быстрее. Вы можете import timeit from timeit, а затем создать метод, в котором есть только for i in xrange: pass и еще один для range, затем выполнить timeit(method1) и timeit(method2), и, о чудо, xrange иногда почти в два раза быстрее (это когда вам не нужен список). (Для меня для i in xrange(1000):pass vs для i in range(1000):pass потребовалось 13.316725969314575 против 21.190124988555908 секунд соответственно - это много.)   -  person dylnmc    schedule 12.11.2014
comment
Другой тест производительности дает xrange(100) на 20% быстрее, чем range(100).   -  person Evgeni Sergeev    schedule 30.05.2015


Ответы (12)


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

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

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

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

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

person Brian    schedule 25.09.2008
comment
Что вы имеете в виду, говоря, что диапазон станет итератором? Разве это не генератор? - person Michael Mior; 26.12.2011
comment
Нет. Генератор относится к определенному типу итератора, и новый range в любом случае не является итератором. - person user2357112 supports Monica; 09.03.2014
comment
Ваша вторая пуля на самом деле не имеет никакого смысла. Вы говорите, что нельзя использовать объект несколько раз; что вы можете! попробуйте xr = xrange(1,11), затем на следующей строке for i in xr: print " ".join(format(i*j,"3d") for j in xr) и вуаля! У вас есть расписания до десяти. Он работает так же, как r = range(1,11) и _4 _... все является объектом в Python2. Я думаю, что вы хотели сказать, что вы можете лучше понять понимание на основе индекса (если это имеет смысл) с range, а не с xrange. Думаю, дальность пригодится очень редко. - person dylnmc; 12.11.2014
comment
(Недостаточно места) Хотя я действительно думаю, что range может быть удобно, если вы хотите использовать list в цикле, а затем изменить определенные индексы в зависимости от определенных условий или добавить элементы в этот список, а затем range определенно лучше. Однако xrange просто быстрее и использует меньше памяти, поэтому для большинства приложений цикла for он кажется лучшим. Бывают случаи - возвращаясь к вопросу вопрошающего - редко, но существуют, когда range было бы лучше. Возможно, не так редко, как я думаю, но я определенно использую xrange 95% времени. - person dylnmc; 12.11.2014

Нет, у них обоих есть свое применение:

Используйте 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
person Dan Lenski    schedule 25.09.2008

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

person Thomas Wouters    schedule 25.09.2008

Еще одно отличие состоит в том, что реализация xrange() в Python 2 не может поддерживать числа, превышающие целые числа 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)
person Brian Minton    schedule 03.08.2012

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

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

person Dan Udey    schedule 25.09.2008

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

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

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

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

person jschultz    schedule 25.09.2008

Я просто хотел бы сказать, что ДЕЙСТВИТЕЛЬНО не так уж и сложно получить объект 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 все равно должен это делать ...

person Garrett Berg    schedule 09.03.2012
comment
Да согласен; из совершенно другой технологии, проверьте работу lodash, чтобы сделать ее ленивой: github.com/lodash/ lodash / issues / 274. Нарезка и т. Д. По-прежнему должна быть как можно более ленивой, а где нет, только затем реифицируйте. - person Rob Grant; 27.08.2014

Хороший пример, приведенный в книге: person Grijesh Chauhan    schedule 17.08.2013


Выбирайте диапазон по следующим причинам:

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

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

person torial    schedule 25.09.2008
comment
Не делай этого. xrange () исчезнет, ​​как и многие другие вещи. Инструмент, который вы будете использовать для перевода кода Python 2.x в код Python 3.x, автоматически переведет xrange () в range (), но range () будет переведен в менее эффективный список (range ()). - person Thomas Wouters; 25.09.2008
comment
Томас: На самом деле это немного умнее. Он будет переводить range () в ситуациях, когда ему явно не нужен реальный список (например, в цикле for или понимании), в простой range (). Только случаи, когда он назначается переменной или используется напрямую, должны быть заключены в list () - person Brian; 25.09.2008

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

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

person Douglas Mayle    schedule 25.09.2008
comment
Это не правда. Код типа x в диапазоне (20) останется как диапазон, а код типа x = range (20) будет преобразован в x = list (range (20)) - ошибок нет. Кроме того, если вы хотите написать код, который будет работать как под 2.6, так и под 3.0, range () - ваш единственный вариант без добавления функций совместимости. - person Brian; 26.09.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, и в Python 3 отсутствует термин xrange(). range() может быть быстрее в некоторых сценариях, если вы повторяете одну и ту же последовательность несколько раз. xrange() должен каждый раз восстанавливать целочисленный объект, но range() будет иметь реальные целочисленные объекты.

person Tushar.PUCSD    schedule 10.09.2016

Хотя в большинстве случаев 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, но если вы не используете ограниченное оборудование, не беспокойтесь об этом слишком сильно.

person speedplane    schedule 18.01.2017
comment
Ваш list_len не используется, поэтому вы используете этот код только для списков длиной 100. - person Mark; 21.03.2017
comment
Я бы порекомендовал изменить длину списка: rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000) - person Mark; 21.03.2017
comment
Вау, похоже, это будет хорошая неделя, спасибо, исправлено. Тем не менее, это не большая разница. - person speedplane; 21.03.2017