2008-09-25 3 views
436

Почему или почему нет?Должны ли вы всегда поддерживать xrange() над диапазоном()?

+35

Может ли кто-нибудь кратко описать разницу между двумя парнями, не являющимися питонами, для нас? Возможно, что-то вроде «xrange() делает каждый диапазон range(), но также поддерживает X, Y и Z». – 2008-09-25 20:45:57

+85

range (n) создает список, содержащий все целые числа 0..n-1. Это проблема, если вы используете диапазон (1000000), потому что в итоге вы получите список> 4Mb. xrange связывается с этим, возвращая объект, который претендует на роль списка, но просто вычисляет число, необходимое из запрошенного индекса, и возвращает это. – Brian 2008-09-25 21:08:08

+4

См. Http://stackoverflow.com/questions/94935/ – 2012-04-13 18:48:00

ответ

425

Для исполнения, особенно при повторении в большом диапазоне, обычно лучше 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) 

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

13

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

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

42

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

3

Берите с диапазоном по этим причинам:

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

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

+13

Не делайте этого. xrange() исчезнет, ​​но так будет и многое другое. Инструмент, который вы будете использовать для перевода кода Python 2.x в код Python 3.x, автоматически преобразует xrange() в range(), но range() будет переведен в менее эффективный список (range()). – 2008-09-25 19:14:44

8

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

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

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

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

2

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

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

127

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

Использование 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 
5

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

30

Еще одно различие в том, что 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) 
4

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

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

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

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

2
  • 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() будет иметь действительные целые объекты.

1

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

Смежные вопросы