Почему list (xrange) медленнее range ()?
Я провел несколько тестов и обнаружил, что xrange() намного быстрее, чем range() (что подтверждается также различными вопросами / ответами):
>>> from timeit import timeit
>>> timeit(stmt = 'x = range(1000)', number = 10000)
0.38216601211680734
>>> timeit(stmt = 'x = xrange(1000)', number = 10000)
0.010537726631953959 # xrange is much faster than range
Мне стало любопытно, поэтому я попробовал другой тест, чтобы увидеть, если list(xrange(1000)) будет по-прежнему будет быстрее, чем Просто range(1000):
>>> timeit(stmt = 'x = range(1000)', number = 10000)
0.3858838963796529
>>> timeit(stmt = 'x = list(xrange(1000))', number = 10000)
0.492734766028903 # now, xrange is slower
Это также верно и для других вызовов:
>>> timeit(stmt = 'x = range(1000)', number = 100000)
3.6457308233315757
>>> timeit(stmt = 'x = list(xrange(1000))', number = 100000)
5.722031755612818
Итак, мой вопрос в том, почему list(xrange) значительно медленнее, чем range сам по себе?
Я видел этот вопрос о медлительности list(), dict(), и прочее методы конструктора , так вот почему list(xrange) настолько медленнее?
Используя dis.dis(), я обнаружил, что list(xrange) выполняет больше вычислений, чем range):
>>> dis.dis('x = list(xrange(1000))')
0 SETUP_LOOP 15648 (to 15651)
3 SLICE+2
4 IMPORT_NAME 29545 (29545)
7 LOAD_GLOBAL 30760 (30760)
10 POP_JUMP_IF_FALSE 28257
13 BUILD_LIST 10341
16 <49>
17 <48>
18 <48>
19 <48>
20 STORE_SLICE+1
21 STORE_SLICE+1
>>> dis.dis('x = range(1000)')
0 SETUP_LOOP 15648 (to 15651)
3 SLICE+2
4 POP_JUMP_IF_FALSE 28257
7 BUILD_LIST 10341
10 <49>
11 <48>
12 <48>
13 <48>
14 STORE_SLICE+1
2 ответа:
Конечно,
range()будет быстрее, когда конечный продукт, который вы хотите, - это список всех чисел в диапазоне,rangeделает все это в одном вызове функции. В отличие отlist(xrange()), который обременяет конструкторlist(..)накладными расходами объектаrangeiterator, созданного для итерации над объектомxrange, который должен потреблять конструкторlist(..). В то время какrange()создает список немедленно, без промежуточного потребления итератора... как это можно победить? Основное отличие: 1 вызов функции vs 2 и менее важно 1 глобальный поиск против 2.
range()сразу же составлю список и верну его вам. Поэтому, если у него много элементов, он медленно строится.С другой стороны,
xrange()возвращает непосредственно итератор-подобный объект, который выдает значения, когда вы их просите. Из документов:Xrange (start, stop [, step])
Эта функция очень похожа на range (), но возвращает объект xrange вместо списка. Это непрозрачный тип последовательности, который дает те же значения, что и соответствующий список, фактически не сохраняя их все одновременно. Преимуществоxrange()передrange()минимально (так какxrange()все еще должен создавать значения, когда их просят) , за исключением, когда очень большой диапазон используется на машине с нехваткой памяти или когда все элементы диапазона никогда не используются (например, когда цикл обычно заканчивается разрывом).Итак, в первом примере
xrange()естественно быстрее, а во втором-медленнее, потому что, в то время какrange()уже возвращает список (который не будет преобразован тогда),list(xrange())придется сделать больше работы, чтобы не только производить значения, но и создавать новый список и хранить значения.P. S. Это справедливо для Python 2. В Python 3 вместо этого есть только
range(), который работает точно так же, как Python 2-xrange().