Вы всегда должны отдавать предпочтение xrange (), а не range ()? - PullRequest
454 голосов
/ 25 сентября 2008

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

Ответы [ 12 ]

439 голосов
/ 25 сентября 2008

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

  • В Python 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 (), диапазон остается неизменным.

129 голосов
/ 26 сентября 2008

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

Используйте 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
42 голосов
/ 25 сентября 2008

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

30 голосов
/ 03 августа 2012

Еще одно отличие состоит в том, что 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)
13 голосов
/ 25 сентября 2008

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

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

8 голосов
/ 25 сентября 2008

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

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

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

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

5 голосов
/ 10 марта 2012

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

4 голосов
/ 17 августа 2013

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

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

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

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

3 голосов
/ 25 сентября 2008

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

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

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

2 голосов
/ 18 января 2017

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

...