Почему или почему нет?
Следует ли всегда отдавать предпочтение xrange () перед range ()?
Ответы (12)
Для производительности, особенно когда вы выполняете итерацию в большом диапазоне, обычно лучше xrange()
. Однако есть еще несколько случаев, почему вы можете предпочесть range()
:
В python 3
range()
делает то, что раньше делалxrange()
, аxrange()
не существует. Если вы хотите написать код, который будет работать как на Python 2, так и на Python 3, вы не можете использоватьxrange()
.range()
в некоторых случаях может быть быстрее - например. при повторении одной и той же последовательности несколько раз.xrange()
должен каждый раз восстанавливать целочисленный объект, ноrange()
будет иметь реальные целочисленные объекты. (Однако он всегда будет хуже работать с памятью)xrange()
не может использоваться во всех случаях, когда нужен реальный список. Например, он не поддерживает срезы или какие-либо методы списков.
[Edit] Есть пара сообщений, в которых упоминается, как 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 или в понимании или в случае, когда он уже заключен в список (), диапазон остается неизменным.
range
в любом случае не является итератором.
- person user2357112 supports Monica; 09.03.2014
xr = xrange(1,11)
, затем на следующей строке for i in xr: print " ".join(format(i*j,"3d") for j in xr)
и вуаля! У вас есть расписания до десяти. Он работает так же, как r = range(1,11)
и _4 _... все является объектом в Python2. Я думаю, что вы хотели сказать, что вы можете лучше понять понимание на основе индекса (если это имеет смысл) с range
, а не с xrange
. Думаю, дальность пригодится очень редко.
- person dylnmc; 12.11.2014
range
может быть удобно, если вы хотите использовать list
в цикле, а затем изменить определенные индексы в зависимости от определенных условий или добавить элементы в этот список, а затем range
определенно лучше. Однако xrange
просто быстрее и использует меньше памяти, поэтому для большинства приложений цикла for он кажется лучшим. Бывают случаи - возвращаясь к вопросу вопрошающего - редко, но существуют, когда range
было бы лучше. Возможно, не так редко, как я думаю, но я определенно использую xrange
95% времени.
- person dylnmc; 12.11.2014
Нет, у них обоих есть свое применение:
Используйте 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
Вы должны отдавать предпочтение range()
перед xrange()
только тогда, когда вам нужен реальный список. Например, когда вы хотите изменить список, возвращаемый range()
, или когда вы хотите его разрезать. Для итерации или даже обычной индексации xrange()
будет работать нормально (и обычно намного эффективнее). Есть точка, в которой range()
немного быстрее, чем xrange()
для очень маленьких списков, но в зависимости от вашего оборудования и различных других деталей безубыточность может иметь длину 1 или 2; не о чем беспокоиться. Предпочитаю xrange()
.
Еще одно отличие состоит в том, что реализация xrange()
в Python 2 не может поддерживать числа, превышающие целые числа 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)
xrange()
более эффективен, потому что вместо создания списка объектов он генерирует только один объект за раз. Вместо 100 целых чисел и всех их накладных расходов и списка для их добавления у вас есть только одно целое число за раз. Более быстрая генерация, лучшее использование памяти, более эффективный код.
Если мне специально не нужен список для чего-то, я всегда предпочитаю xrange()
range () возвращает список, xrange () возвращает объект xrange.
xrange () немного быстрее и немного более эффективен с точки зрения памяти. Но выигрыш не очень большой.
Дополнительная память, используемая списком, конечно, не просто тратится впустую, списки имеют больше функций (срез, повтор, вставка и т. Д.). Точные различия можно найти в документации. Нет правила жесткости, используйте то, что нужно.
Python 3.0 все еще находится в разработке, но IIRC range () будет очень похож на xrange () из 2.X, а list (range ()) может использоваться для создания списков.
Я просто хотел бы сказать, что ДЕЙСТВИТЕЛЬНО не так уж и сложно получить объект 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 все равно должен это делать ...
Хороший пример, приведенный в книге: person Grijesh Chauhan schedule 17.08.2013
Выбирайте диапазон по следующим причинам:
1) xrange исчезнет в новых версиях Python. Это дает вам легкую совместимость в будущем.
2) range примет эффективность, связанную с xrange.
Хорошо, у всех здесь разные мнения относительно компромиссов и преимуществ xrange по сравнению с range. Они в основном правильные, xrange - это итератор, а range расширяется и создает фактический список. В большинстве случаев вы не заметите разницы между ними. (Вы можете использовать map с диапазоном, но не с xrange, но он использует больше памяти.)
Однако я думаю, что вы хотите услышать, что предпочтительным выбором является xrange. Поскольку диапазон в Python 3 является итератором, инструмент преобразования кода 2to3 правильно преобразует все использования xrange в диапазон и выдаст ошибку или предупреждение для использования диапазона. Если вы хотите быть уверенным, что в будущем легко конвертируете свой код, вы будете использовать только xrange и list (xrange), если уверены, что вам нужен список. Я узнал об этом во время спринта CPython на PyCon в этом году (2008) в Чикаго.
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, и в Python 3 отсутствует термин xrange()
. range()
может быть быстрее в некоторых сценариях, если вы повторяете одну и ту же последовательность несколько раз. xrange()
должен каждый раз восстанавливать целочисленный объект, но range()
будет иметь реальные целочисленные объекты.
Хотя в большинстве случаев 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
, но если вы не используете ограниченное оборудование, не беспокойтесь об этом слишком сильно.
list_len
не используется, поэтому вы используете этот код только для списков длиной 100.
- person Mark; 21.03.2017
rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000)
- person Mark; 21.03.2017
range(1000)
- этоlist
,xrange(1000)
- это объект, который действует какgenerator
(хотя, конечно, не). Кроме того,xrange
быстрее. Вы можетеimport timeit from timeit
, а затем создать метод, в котором есть толькоfor i in xrange: pass
и еще один дляrange
, затем выполнитьtimeit(method1)
иtimeit(method2)
, и, о чудо, xrange иногда почти в два раза быстрее (это когда вам не нужен список). (Для меня дляi in xrange(1000):pass
vs дляi in range(1000):pass
потребовалось13.316725969314575
против21.190124988555908
секунд соответственно - это много.) - person dylnmc   schedule 12.11.2014xrange(100)
на 20% быстрее, чемrange(100)
. - person Evgeni Sergeev   schedule 30.05.2015