Почему в 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,538883924482) 3,215819835662842

Это почему? Я имею в виду, почему Xrange был удален? Это такой отличный инструмент для изучения. Для начинающих, как и я, как и все мы были в какой-то момент. Зачем удалять это? Может кто-нибудь указать мне на правильный ПКП, я не могу его найти.

Приветствия.

6 ответов

Решение

Некоторые измерения производительности, используя 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-bit:

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, И ОП xrange функция не имеет к этому никакого отношения. (Не удивительно, как разовый звонок на __iter__ слот вряд ли будет виден среди 10000000 вызовов к тому, что происходит в цикле, но кто-то поднял это как возможность.)

Но это только на 30% медленнее. Как ОП получил 2х раз медленнее? Что ж, если я повторю те же тесты с 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.3range и 2,7xrange, Вы даже можете увидеть историю изменений (я думаю, что это связано с изменением, которое заменило последний экземпляр строки "xrange" в любом месте файла).

Итак, почему это медленнее?

Ну, например, они добавили много новых функций. С другой стороны, они сделали все виды изменений повсеместно (особенно внутри итерации), которые имеют незначительные побочные эффекты. И было проделано много работы, чтобы значительно оптимизировать различные важные случаи, даже если иногда они слегка пессимизируют менее важные случаи. Добавьте все это, и я не удивлен, что итерация range как можно быстрее, теперь немного медленнее. Это один из тех менее важных случаев, на которых никто никогда не будет обращать внимания. Ни у кого, вероятно, никогда не будет реального сценария использования, где эта разница в производительности является горячей точкой в ​​их коде.

Диапазон Python3 - это xrange Python2. Там нет необходимости обернуть его вокруг него. Чтобы получить актуальный список в Python3, вам нужно использовать list(range(...))

Если вы хотите что-то, что работает с Python2 и Python3, попробуйте это

try:
    xrange
except NameError:
    xrange = range

Python 3 range тип работает так же, как 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

Я недавно узнал, что Python 3 range Тип имеет некоторые другие полезные функции, такие как поддержка нарезки: range(10,100,2)[5:25:5] является range(15, 60, 10)!

Один из способов исправить код на python2:

import sys

if sys.version_info >= (3, 0):
    def xrange(*args, **kwargs):
        return iter(range(*args, **kwargs))

xrange из Python 2 является генератором и реализует итератор, в то время как range это просто функция. В Python3 я не знаю, почему пропал xrange.

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

С номером time =1 параметр:

>>> 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 (по умолчанию, 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.

Другие вопросы по тегам