В чем разница между функциями range и xrange в Python 2.X?

719

Очевидно, что xrange быстрее, но я понятия не имею, почему он быстрее (и нет никаких доказательств, кроме того, что он раньше был быстрее), или что кроме этого отличается

for i in range(0, 20):
for i in xrange(0, 20):
Teifion
источник

Ответы:

817

В Python 2.x:

  • rangeсоздает список, поэтому, если вы это сделаете, range(1, 10000000)он создает список в памяти с 9999999элементами.

  • xrange является объектом последовательности, который оценивает лениво.

В Python 3 rangeделает эквивалент Python xrange, и чтобы получить список, вы должны использовать list(range(...)).

Чарльз
источник
65
xrange - это не просто генератор, но он вычисляет лениво и действует как генератор.
Вайбхав Мишра
47
xrange(x).__iter__()это генератор.
augustomen
34
Почему они сделали xrange, а не сделали диапазон ленивым?
Роб Грант
22
@RobertGrant, они сделали. В Python 3. (Они не могли этого сделать в строке Python 2.x, поскольку все изменения должны быть обратно совместимы.)
Пол Дрейпер
12
@Ratul означает, что каждый iоценивается по требованию, а не при инициализации.
Онилол,
223

range создает список, поэтому, если вы это сделаете, range(1, 10000000)он создает список в памяти с 9999999элементами.

xrange является генератором, так что это объект последовательности , который оценивает лениво.

Это правда, но в Python 3 .range()будет реализован Python 2 .xrange(). Если вам нужно сгенерировать список, вам нужно сделать:

list(range(1,100))
Corey
источник
3
Я не вижу, чтобы это была огромная проблема (в отношении взлома существующих приложений), поскольку диапазон был в основном для генерации индексов, которые будут использоваться для циклов как «для i в диапазоне (1, 10):»
Бенджамин Аутин,
10
+1 Спасибо за этот ответ, информация о замене диапазона в Python 3 на xrange очень полезна. Я на самом деле сказал кому-то использовать xrange вместо или range, и они сказали, что это не имеет значения в python 3, поэтому я поискал в Google дополнительную информацию, и этот ответ появился :)
Cervo
Что не так с вызовом xrangeгенератора? Это функция, содержащая yieldоператор, и согласно глоссарию такие функции называются генераторами.
winterlight
@winterlight, думаю, что правильный термин для этого - итератор. Генераторы должны быть в состоянии получить также.
Максиникс
112

Помните, используйте timeitмодуль, чтобы проверить, какой из небольших фрагментов кода быстрее!

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

Лично я всегда использую .range(), если я не имел дело с действительно огромными списками - как вы видите, с точки зрения времени, для списка из миллиона записей дополнительные издержки составляют всего 0,04 секунды. И, как указывает Кори, в Python 3.0 .xrange()он исчезнет и в .range()любом случае даст вам хорошее поведение итератора.

Джон Фухи
источник
12
+1 за пример времени. Примечание: для запуска в Windows cmd необходимо использовать двойные кавычки, т.е. ". Таким образом, код будетpython -m timeit "for i in xrange(1000000):" " pass"
stalk
10
Основным преимуществом xrange является память, а не время.
эндолит
3
+1 за практический ответ: используйте диапазон, если не огромный . Кстати они концептуально идентичны, правильно? Как ни странно, никакой ответ не говорит об этом.
Боб Стейн
6
Если xrange быстрее и не забирает память, зачем вообще использовать диапазон?
Остин Мор
8
Я в целом согласен с вашим утверждением, но ваша оценка неверна: the extra overhead is only 0.04 secondsэто неправильный взгляд на него, (90.5-51.1)/51.1 = 1.771 times slowerон правильный, поскольку он говорит о том, что, если это основной цикл вашей программы, он потенциально может стать узким местом. Однако, если это небольшая часть, то 1,77х - это немного.
chacham15
65

xrangeсохраняет только параметры диапазона и генерирует числа по запросу. Однако реализация Python на C в настоящее время ограничивает свои аргументы C longs:

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

Обратите внимание, что в Python 3.0 есть только rangeи он ведет себя как 2.x, xrangeно без ограничений на минимальные и максимальные конечные точки.

efotinis
источник
39

xrange возвращает итератор и одновременно хранит только одно число в памяти. Диапазон хранит весь список чисел в памяти.

Бен Хоффштейн
источник
9
xrangeничего не возвращает итератор.
abarnert
and only keeps one number in memory at a timeи где остальные размещены, пожалуйста,
наведите
5
@Sslam Если он знает начало, конец и ток, он может вычислить следующее, по одному за раз.
Джастин Майнерс
30

Потратьте некоторое время на Библиотечный справочник . Чем более вы знакомы с ним, тем быстрее вы сможете найти ответы на подобные вопросы. Особенно важны первые несколько глав о встроенных объектах и ​​типах.

Преимущество типа xrange заключается в том, что объект xrange всегда будет занимать одинаковый объем памяти, независимо от размера диапазона, который он представляет. Там нет последовательных преимуществ производительности.

Другой способ найти быструю информацию о конструкции Python - это строка документации и функция справки:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)
Антти Расинен
источник
1
Библиотека хорошая, но не всегда так просто получить ответ на свой вопрос.
Teifion
2
Перейдите к справочнику по библиотеке, нажмите Ctrl + F, найдите диапазон, и вы получите два результата. Не так много усилий, чтобы найти ответ на этот вопрос.
Дэвид Локк
1
Ссылка на библиотеку не работает. Можете ли вы обновить его?
MK ..
14

Я в шоке, никто не читал док :

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

Кишор Павар
источник
13

range создает список, поэтому, если вы сделаете range (1, 10000000), он создаст список в памяти с 10000000 элементов. xrange - генератор, поэтому он оценивает лениво.

Это дает вам два преимущества:

  1. Вы можете перебирать более длинные списки, не получая MemoryError.
  2. Поскольку каждое число лениво разрешается, если вы остановите итерацию раньше, вы не будете тратить время на создание всего списка.
Лукас С.
источник
12

Вы найдете преимущество xrangeперед rangeэтим простым примером:

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds

Приведенный выше пример не отражает ничего существенно лучшего в случае xrange.

Теперь посмотрим на следующий случай, когда rangeон действительно очень медленный по сравнению с xrange.

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer() 

print "time taken: ", (t2-t1)  # 2.78506207466 seconds

С помощью rangeон уже создает список от 0 до 100000000 (отнимает много времени), но xrangeявляется генератором и генерирует числа только на основе потребности, то есть, если итерация продолжается.

В Python-3 реализация rangeфункциональности такая же, как и xrangeв Python-2, в то время как xrangeв Python-3 они покончили с этим.

Удачного кодирования!

User_Targaryen
источник
11

Это из соображений оптимизации.

range () создаст список значений от начала до конца (0 .. 20 в вашем примере). Это станет дорогой операцией на очень больших диапазонах.

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

QAZ
источник
9

range(x,y)возвращает список каждого числа между x и y, если вы используете forцикл, то rangeмедленнее. На самом деле, rangeимеет больший диапазон индекса. range(x.y)распечатает список всех чисел между x и y

xrange(x,y)возвращает, xrange(x,y)но если вы использовали forцикл, то xrangeбыстрее. xrangeимеет меньший диапазон индекса. xrangeбудет не только распечатывать, xrange(x,y)но и сохранять все числа, которые в нем есть.

[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)

Если вы используете forцикл, то это будет работать

[In] for i in range(1,10):
        print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9
[In] for i in xrange(1,10):
         print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9

При использовании петель нет большой разницы, хотя есть разница только при печати!

Supercolbat
источник
8

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__

xrange([start,] stop[, step]) -> xrange object
Tushar.PUCSD
источник
6

В некоторых других ответах упоминается, что Python 3 исключил 2.x rangeи переименовал 2.x xrangeв range. Однако, если вы не используете 3.0 или 3.1 (что никто не должен делать), это на самом деле несколько другой тип.

Как сказано в документах 3.1 :

Объекты Range имеют очень небольшое поведение: они поддерживают только индексацию, итерацию и lenфункцию.

Тем не менее, в 3.2+, rangeэто полная последовательность - она ​​поддерживает расширенные слайсы и все методы collections.abc.Sequenceс той же семантикой, что и a list. *

И, по крайней мере , в CPython и PyPy (только два 3.2+ реализации , которые в настоящее время существуют), он также имеет реализации постоянная времени проведения indexи countметоды и inоператор ( до тех пор , как вы только передать его целые числа). Это означает, что писать 123456 in rв 3,2+ разумно, а в 2,7 или 3,1 было бы ужасной идеей.


* Тот факт, что issubclass(xrange, collections.Sequence)возвращается Trueв 2.6-2.7 и 3.0-3.1, является ошибкой, которая была исправлена ​​в 3.2 и не портирована.

abarnert
источник
6

В Python 2.x

range (x) возвращает список, созданный в памяти с x элементами.

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]

xrange (x) возвращает объект xrange, который является генератором obj, который генерирует числа по требованию. они вычисляются во время цикла for (Lazy Evaluation).

Для цикла это немного быстрее, чем range () и более эффективно использовать память.

>>> b = xrange(5)
>>> b
xrange(5)
Сиярам Малав
источник
xrange()не генератор xrange(n).__ iter __ () `есть.
th3an0maly
5

При тестировании диапазона против xrange в цикле (я знаю, что должен использовать timeit , но это было быстро взломано из памяти с помощью простого примера понимания списка), я обнаружил следующее:

import time

for x in range(1, 10):

    t = time.time()
    [v*10 for v in range(1, 10000)]
    print "range:  %.4f" % ((time.time()-t)*100)

    t = time.time()
    [v*10 for v in xrange(1, 10000)]
    print "xrange: %.4f" % ((time.time()-t)*100)

который дает:

$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?

Или, используя xrange в цикле for:

range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509

Правильно ли тестируется мой фрагмент? Любые комментарии по поводу медленного экземпляра xrange? Или лучший пример :-)

Дейв Эверитт
источник
2
Однократный запуск такого теста не дает точных результатов по времени. Разница всегда есть .. Это может быть либо GC, либо другой процесс, крадущий процессор ... что угодно. Вот почему тесты обычно проводятся 10-100-1000 -...
Вайк Хермеч
это просто поспешная распечатка фрагмента - я запускал ее несколько раз, но только до 100, и xrangeвыглядел немного быстрее, хотя с Python 3 сравнение теперь излишне.
Дейв Эверитт
3
Это для чего timeit. Он многократно работает, отключает GC, использует лучшие часы вместо timeи т. Д.
abarnert
4

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

Когда мы используем range (), мы выделяем память для всех переменных, которые он генерирует, поэтому не рекомендуется использовать с большим значением no. переменных, которые будут сгенерированы.

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

Лакшая Махешвари
источник
3

Range генерирует весь список и возвращает его. xrange не делает - генерирует числа в списке по запросу.

Эдди Дейо
источник
2

xrange использует итератор (генерирует значения на лету), range возвращает список.

hacama
источник
2

Какая?
rangeвозвращает статический список во время выполнения.
xrangeвозвращает object(который действует как генератор, хотя, конечно, не один), из которого генерируются значения, как и когда это необходимо.

Когда использовать что?

  • Используйте, xrangeесли вы хотите создать список для гигантского диапазона, скажем, 1 миллиард, особенно если у вас есть «чувствительная к памяти система», такая как мобильный телефон.
  • Используйте, rangeесли вы хотите перебрать список несколько раз.

PS: Python 3.x в rangeфункции == Python 2.x в xrangeфункции.

kmario23
источник
xrangeне возвращает объект генератора.
abarnert
Если я правильно понимаю, это объясняется здесь (для Python 2.x): wiki.python.org/moin/Generators
kmario23
Тогда вики не права. (Я не знаю, кто такой «SH», кто добавил и подписал этот комментарий.) Официальная документация верна; Вы можете проверить это самостоятельно и посмотреть, является ли это генератором или последовательностью.
abarnert
Хорошо. Но это все еще сбивает с толку после прочтения этого: stackoverflow.com/questions/135041/…
kmario23
1
Забавный вопрос в том, что делать, когда переводчик не согласен с официальными документами или с другим переводчиком ... Но, к счастью, это не так часто встречается ...
abarnert
2

Все это очень хорошо объяснили. Но я хотел увидеть это сам. Я использую python3. Итак, я открыл монитор ресурсов (в Windows!) И сначала выполнил следующую команду:

a=0
for i in range(1,100000):
    a=a+i

а затем проверил изменения в памяти «В использовании». Это было незначительно. Затем я запустил следующий код:

for i in list(range(1,100000)):
    a=a+i

И это заняло большой кусок памяти для использования, мгновенно. И я был убежден. Вы можете попробовать это для себя.

Если вы используете Python 2X, замените range () на xrange () в первом коде, а list (range ()) на range ().

АНКУР САТЯ
источник
2

С помощью документов.

Python 2.7.12

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is 
slightly faster than range() and more memory efficient.

Python 3.5.2

>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

Разница очевидна. В Python 2.x rangeвозвращает список, xrangeвозвращает объект xrange, который является итеративным.

В Python 3.x rangeстановится xrangePython 2.x и xrangeудаляется.

Раджендра Уппал
источник
1

По требованию к сканированию / печати 0-N предметов, range и xrange работает следующим образом.

range () - создает новый список в памяти, принимает от 0 до N элементов (всего N + 1) и печатает их. xrange () - создает экземпляр итератора, который просматривает элементы и сохраняет в памяти только текущий обнаруженный элемент, следовательно, все время используя один и тот же объем памяти.

Если требуемый элемент находится в начале списка только тогда, он экономит много времени и памяти.

SomeDoubts
источник
1
xrangeне создает экземпляр итератора. Он создает xrangeобъект, который является итеративным, но не итератором, почти (но не совсем) последовательностью, подобной списку.
abarnert
1

Range возвращает список, в то время как xrange возвращает объект xrange, который занимает одну и ту же память независимо от размера диапазона, поскольку в этом случае генерируется только один элемент, и он доступен для каждой итерации, тогда как в случае использования диапазона все элементы генерируются одновременно и доступны в памяти.

user299567
источник
1

Разница уменьшается при меньших аргументах в range(..)/ xrange(..):

$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop

В этом случае xrange(100)только на 20% эффективнее.

Евгений Сергеев
источник
1

range: -range заполнит все сразу. Это означает, что каждое число диапазона будет занимать память.

xrange: -xrange - это что-то вроде генератора, он появляется, когда вы хотите диапазон чисел, но вы не хотите, чтобы они были сохранены, например, когда вы хотите использовать эффективную память для loop.so.

Теджасвини Теджу
источник
1

Кроме того, если сделать list(xrange(...))будет эквивалентно range(...).

Так listмедленно.

Также на xrangeсамом деле не полностью закончить последовательность

Так вот почему это не список, это xrangeобъект

U10-Forward
источник
1

range() в Python 2.x

Эта функция по сути является старой range()функцией, которая была доступна в Python, 2.xи возвращает экземпляр listобъекта, который содержит элементы в указанном диапазоне.

Однако эта реализация слишком неэффективна, когда речь идет об инициализации списка с диапазоном чисел. Например, for i in range(1000000)это будет очень дорогая команда для выполнения, как с точки зрения использования памяти, так и времени, так как она требует сохранения этого списка в памяти.


range()в Python 3.xи xrange()в Python2.x

Python 3.xпредставил более новую реализацию range()(в то время как более новая реализация уже была доступна в Python 2.xчерез xrange()функцию).

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


В качестве примера рассмотрим следующее:

# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>

а также

# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>
Гиоргос Мириантос
источник
-2

Смотрите этот пост, чтобы найти разницу между range и xrange:

Цитировать:

rangeвозвращает именно то, что вы думаете: список последовательных целых чисел определенной длины, начинающихся с 0. xrangeОднако возвращает «объект xrange» , который очень похож на итератор

Око
источник
2
Я понимаю, что это 5 лет, но этот пост ошибается почти во всем. xrangeне является итератором. Список, возвращаемый rangeфункцией действительно поддерживает итерацию (список является в значительной степени прототипом примера итерации). Общая выгода xrangeне является "минимальной". И так далее.
abarnert