Яка різниця між діапазоном і функцією 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.