Почему в Python3 нет функции xrange?
Недавно я начал использовать Python3, и у меня не было проблем с xrange.
Простой пример:
1) Python2:
from time import time as t
def count():
st = t()
[x for x in xrange(10000000) if x%4 == 0]
et = t()
print et-st
count()
2) Python3:
from time import time as t
def xrange(x):
return iter(range(x))
def count():
st = t()
[x for x in xrange(10000000) if x%4 == 0]
et = t()
print (et-st)
count()
Результаты, соответственно:
1) 1.53888392448
2) 3.215819835662842
Почему? Я имею в виду, почему xrange был удален? Это такой отличный инструмент для изучения. Для новичков, как и я, как и все мы в какой-то момент. Зачем его удалять? Может кто-нибудь указать мне на правильный PEP, я не могу его найти.
Приветствия.
Ответы
Ответ 1
Некоторые измерения производительности, используя timeit
вместо того, чтобы делать это вручную с помощью time
.
Во-первых, Apple 2.7.2 64-бит:
In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.05 s per loop
Теперь, python.org 3.3.0 64-бит:
In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.32 s per loop
In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.31 s per loop
In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.33 s per loop
По-видимому, 3.x range
действительно немного медленнее 2.x xrange
. И функция OP xrange
не имеет к этому никакого отношения. (Не удивительно, что одноразовый вызов слота __iter__
вряд ли будет отображаться среди 10000000 звонков на все, что происходит в цикле, но кто-то поднял его как возможность.)
Но это только на 30% медленнее. Как OP получал 2x так же медленно? Хорошо, если я повторю те же тесты с 32-битным Python, я получаю 1.58 против 3.12. Поэтому я предполагаю, что это еще один из тех случаев, когда 3.x оптимизирован для 64-разрядной производительности способами, которые вредят 32-разрядным.
Но действительно ли это имеет значение? Проверьте это, с 3.3.0 64-бит снова:
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop
Итак, построение list
занимает более чем вдвое больше, чем вся итерация.
И что касается "потребляет гораздо больше ресурсов, чем Python 2.6+", то из моих тестов это выглядит как 3.x range
точно такого же размера, как 2.x xrange
, и даже если это были в 10 раз больше, создание ненужного списка по-прежнему на 10000000 пикселей больше, чем что-либо, что может сделать итерация диапазона.
А как насчет явного цикла for
вместо цикла C внутри deque
?
In [87]: def consume(x):
....: for i in x:
....: pass
In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
1 loops, best of 3: 1.85 s per loop
Итак, почти столько же времени потрачено в инструкции for
, как в действительной работе итерации range
.
Если вы беспокоитесь об оптимизации итерации объекта диапазона, вы, вероятно, смотрите не в то место.
Между тем, вы продолжаете спрашивать, почему xrange
был удален, независимо от того, сколько раз люди говорят вам одно и то же, но я повторю его еще раз: он не был удален: он был переименован в range
, а 2.x range
- это то, что было удалено.
Здесь некоторое доказательство того, что объект 3.3 range
является прямым потомком объекта 2.x xrange
(а не функции 2.x range
): источник 3.3 range
и 2.7 xrange
. Вы даже можете увидеть историю изменений (связанная, по моему мнению, сменой, которая заменила последний экземпляр строки "xrange" в любом месте файла).
Итак, почему он медленнее?
Ну, во-первых, они добавили много новых функций. С другой стороны, они сделали всевозможные изменения повсюду (особенно внутри итерации), которые имеют незначительные побочные эффекты. И было много работы, чтобы резко оптимизировать различные важные случаи, даже если это иногда слегка пессимизирует менее важные случаи. Добавьте все это, и я не удивлюсь, что итерация range
как можно быстрее будет немного медленнее. Это один из тех менее важных случаев, о которых никто никогда не заботится, чтобы сосредоточиться. В любом случае, никто не будет иметь реальный случай использования, когда это различие в производительности является точкой доступа в их коде.
Ответ 2
Диапазон Python3 - это Python2 xrange. Там нет необходимости обертывать вокруг себя. Чтобы получить фактический список в Python3, вам нужно использовать list(range(...))
Если вы хотите что-то, что работает с Python2 и Python3, попробуйте это
try:
xrange
except NameError:
xrange = range
Ответ 3
Тип range
Python 3 работает так же, как и Python 2 xrange
. Я не уверен, почему вы видите замедление, поскольку итератор, возвращаемый вашей функцией xrange
- это именно то, что вы получите, если бы вы итерировали по range
напрямую.
Я не могу воспроизвести замедление в моей системе. Вот как я тестировал:
Python 2, с xrange
:
Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
18.631936646865853
Python 3 с range
немного быстрее:
Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
17.31399508687869
Недавно я узнал, что у типа range
Python 3 есть и другие полезные функции, такие как поддержка нарезки: range(10,100,2)[5:25:5]
is range(20, 60, 10)
!
Ответ 4
Один из способов исправить ваш код python2:
import sys
if sys.version_info >= (3, 0):
def xrange(*args, **kwargs):
return iter(range(*args, **kwargs))
Ответ 5
xrange из Python 2 является генератором и реализует итератор, в то время как range это просто функция. В Python3 я не знаю, почему выпал Xrange.
Ответ 6
comp: ~ $python
Python 2.7.6 (по умолчанию, 22 июня 2015, 17:58:13)
[GCC 4.8.2] на linux2
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
5,656799077987671
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
5,579368829727173
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
+21,54827117919922
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
+22,014557123184204
С номером timeit = 1 param:
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)
0,2245171070098877
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=1)
+0,10750913619995117
comp: ~ $python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] в linux
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
9,113872020003328
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
+9,07014398300089
С временным номером = 1,2,3,4 параметр работает быстро и линейно:
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)
+0,09329321900440846
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=2)
+0,18501482300052885
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=3)
0,2703447980020428
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=4)
+0,36209142999723554
Итак, если мы измеряем 1 цикл цикла цикла, например timeit.timeit( "[x для x в диапазоне (1000000), если x% 4]", число = 1) (как мы фактически используем в реальном коде) python3 работает достаточно быстро, но в повторяющихся циклах python 2 xrange() выигрывает по скорости от range() от python 3.