Яка різниця між діапазоном і функцією xrange в Python 2.X?


718

Мабуть, xrange - це швидше, але я поняття не знаю, чому це швидше (і жодного доказу, крім анекдотичного поки що, що швидше) або що, крім цього, відрізняється

for i in range(0, 20):
for i in xrange(0, 20):

Відповіді:


817

У Python 2.x:

  • rangeстворює список, тож якщо ви range(1, 10000000)це робите, він створює список в пам'яті з 9999999елементами.

  • xrange є об'єктом послідовності, який оцінюється ліниво.

У Python 3 rangeє еквівалент python's xrange, і щоб отримати список, ви повинні використовувати list(range(...)).


65
xrange - це не точно генератор, але він ліниво оцінює і діє як генератор.
Вайбхав Мішра

47
xrange(x).__iter__()є генератором.
серпень

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))

3
Я не бачу, що це була велика проблема (щодо розбиття існуючих додатків), оскільки діапазон був здебільшого для генерування індексів, які будуть використовуватися для циклів як "для i в діапазоні (1, 10):"
Бенджамін Остін,

10
+1 Дякую за цю відповідь, інформація про заміну діапазону Python 3 на xrange дуже корисна. Я фактично сказав комусь використовувати xrange замість або діапазон, і вони сказали, що це не має значення в python 3, тому я google шукав додаткову інформацію, і ця відповідь прийшла :)
Cervo

Що не так з викликом xrangeгенератора? Це функція, що містить yieldоператор, і згідно з глосарієм такі функції називаються генераторами.
зимове світло

@Winterlight, подумайте, що правильний термін для нього - це ітератор. Генератори також повинні мати можливість приймати.
McSinyx

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 секунди. І як зазначає Corey, в Python 3.0 .xrange()піде все-таки і .range()надасть вам приємного поведінки ітератора.


12
+1 для прикладу timeit. Примітка: для запуску у Windows cmd потрібно використовувати подвійну цитату, тобто ". Отже, код будеpython -m timeit "for i in xrange(1000000):" " pass"
стебло

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,77x не дуже.
чача15

65

xrangeзберігає лише параметри діапазону та генерує номери за запитом. Однак C-реалізація Python в даний час обмежує свої аргументи на 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але без обмежень на мінімальну та максимальну кінцеві точки.


39

xrange повертає ітератор і одночасно зберігає лише одне число. діапазон зберігає весь список номерів у пам'яті.


9
xrangeнічого НЕ повертає ітератор.
abarnert

and only keeps one number in memory at a timeа де розміщені інші, будь ласка, направляйте мене ..
SIslam

5
@SIslam Якщо він знає початок, кінець та поточний, він може обчислити наступний, по одному.
Джастін Майнерс

30

Проведіть деякий час з довідкою про бібліотеку . Чим більше ви з цим знайомі, тим швидше ви зможете знайти відповіді на подібні питання. Особливо важливими є перші кілька глав про вбудовані об'єкти та типи.

Перевага типу xrange полягає в тому, що об’єкт xrange завжди займе однаковий об'єм пам'яті, незалежно від розміру діапазону, який він представляє. Немає послідовних переваг у виконанні.

Інший спосіб знайти швидку інформацію про конструкцію Python - це docstring та довідкова функція:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)

1
Бібліотека хороша, але не завжди так легко отримати відповідь на поставлене вами питання.
Тейфіон

2
Перейдіть до посилання на бібліотеку, натисніть ctrl + f, знайдіть діапазон, і ви отримаєте два результати. Немало зусиль, щоб знайти відповідь на це питання.
Девід Локк

1
Посилання на бібліотеку не працює. Чи можете ви оновити його?
mk ..

14

Я вражений, що ніхто не читав doc :

Ця функція дуже схожа на range(), але повертає xrangeоб'єкт замість списку. Це непрозорий тип послідовностей, який дає ті самі значення, що і відповідний список, фактично не зберігаючи їх одночасно. Перевага xrange()надrange() мінімальною (оскільки xrange()все-таки доводиться створювати значення, коли їх запитують), за винятком випадків, коли на машині з голодом пам’яті використовується дуже великий діапазон або коли всі елементи діапазону ніколи не використовуються (наприклад, коли цикл є зазвичай припиняється break).


13

діапазон створює список, тож якщо ви робите діапазон (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 вони не виконали

Щасливе кодування !!


11

Це з міркувань оптимізації.

range () створить список значень від початку до кінця (0 .. 20 у вашому прикладі). Це стане дорогою операцією на дуже великих діапазонах.

xrange (), з іншого боку, значно оптимізованіший. він буде обчислювати наступне значення лише при необхідності (через об'єкт послідовності xrange) і не створюватиме список усіх значень, таких як range ().


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

Існує не велика різниця при використанні циклів, хоча є різниця, коли просто друкуємо його!


8

range (): діапазон (1, 10) повертає список від 1 до 10 чисел і зберігає весь список у пам'яті.

xrange (): як діапазон (), але замість повернення списку повертає об'єкт, який генерує числа в діапазоні на вимогу. Для циклічного циклу це трохи швидше, ніж діапазон () та більш ефективна пам'ять. xrange () об'єкт, як ітератор, і генерує числа за запитом. (Lazy Evaluation)

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

6

Деякі з інших відповідей зазначити , що Python 3 усунутий 2.x - х rangeі перейменований 2.x років xrangeв range. Однак, якщо ви не використовуєте 3.0 або 3.1 (чого ніхто не повинен бути), це насправді дещо інший тип.

Як кажуть 3.1 документа :

Об'єкти діапазону мають дуже мало поведінки: вони підтримують лише індексацію, ітерацію та 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 та не підтримувалася.


6

У пітоні 2.х

range (x) повертає список, створений в пам'яті з елементами x.

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

xrange (x) повертає об'єкт xrange, який є генератором obj, який генерує числа за запитом. вони обчислюються під час циклу for-loop (Ледача оцінка).

Для циклічного циклу це трохи швидше, ніж діапазон () та більш ефективна пам'ять.

>>> 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 (), ми виділяємо пам’ять на всі змінні, які вона генерує, тому не рекомендується використовувати з більшими ні. змінних, які потрібно генерувати.

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


3

діапазон генерує весь список і повертає його. xrange не робить - вона генерує номери в списку на вимогу.


2

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


2

Що?
rangeповертає статичний список під час виконання.
xrangeповертаєobject (який діє як генератор, хоча він, звичайно, не один), з якого формуються значення як і коли потрібно.

Коли використовувати який?

  • Використовуйте xrange якщо ви хочете створити список для гігантського діапазону, скажімо, 1 мільярд, особливо коли у вас є "чутлива до пам'яті система", як мобільний телефон.
  • Використовуйте, rangeякщо ви хочете повторити список кілька разів.

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


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

а потім перевірити зміни в пам'яті "In Use". Це було незначно. Потім я запустив такий код:

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

І це миттєво зайняло великий шматок пам'яті. І, я переконався. Ви можете спробувати на собі.

Якщо ви використовуєте Python 2X, замініть 'range ()' на 'xrange ()' у першому коді, а 'list (range ())' на 'range ()'.


2

З довідкових док.

Пітон 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.

Пітон 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 елементів, діапазон і xrange працює наступним чином.

range () - створює новий список у пам'яті та займає цілі 0 до N елементів (повністю N + 1) та друкує їх. xrange () - створює ітераторський екземпляр, який сканує елементи та зберігає в пам’яті лише поточний зустрічається елемент, отже, весь час використовує однаковий об’єм пам’яті.

Якщо потрібний елемент дещо знаходиться на початку списку, лише тоді він економить багато часу та пам'яті.


1
xrangeне створює ітератор. Він створює xrangeоб'єкт, який є ітерабельним, але не є ітератором - майже (але не зовсім) послідовністю, як список.
abarnert

1

Діапазон повертає список, тоді як xrange повертає об’єкт xrange, який займає ту саму пам'ять, незалежно від розміру діапазону, як у цьому випадку, генерується лише один елемент та доступний за ітерацію, тоді як у разі використання діапазону всі елементи генеруються одразу та доступні в пам'яті.


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 буде заповнювати все одразу. Що означає, що кожне число діапазону займе пам'ять.

xrange: -range є чимось на зразок генератора, він з'явиться на малюнку, коли ви хочете, щоб діапазон чисел, але ви не хочете, щоб вони зберігалися, як, коли ви хочете використовувати в loop.so ефективної пам'яті.


1

Крім того, якщо робити, list(xrange(...))це буде еквівалентноrange(...) .

Тому list повільно.

Також xrange дійсно не повністю закінчується послідовність

Тож тому його не список, а xrangeоб’єкт


1

range() в Python 2.x

Ця функція по суті є старою range()функцією, яка була доступна в Python 2.xі повертає екземпляр alist об'єкта, який містить елементи у вказаному діапазоні.

Однак ця реалізація є занадто неефективною, коли мова йде про ініціалізацію списку з діапазоном чисел. Наприклад, 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

Дивіться цю публікацію щоб знайти різницю між діапазоном і xrange:

Цитувати:

rangeповертає саме те, що ви думаєте: список послідовних цілих чисел, визначеної довжини, що починається з 0. xrange, однак повертає "xrange об'єкт" , який діє дуже як ітератор


2
Я усвідомлюю, що цьому вже 5 років, але ця публікація помиляється майже в усьому. xrangeне є ітератором. Список, що повертається, rangeпідтримує ітерацію (список - це майже прототипний приклад ітерабельності). Загальна вигода від xrange"не мінімальна". І так далі.
abarnert
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.