¿Por qué no hay una función xrange en Python3?

Resuelto catalesia asked hace 11 años • 6 respuestas

Recientemente comencé a usar Python3 y su falta xrangeduele.

Ejemplo sencillo:

  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()
    

Los resultados son, respectivamente:

  1. 1.53888392448
  2. 3.215819835662842

¿Porqué es eso? Quiero decir, ¿por qué xrangese ha eliminado? Es una gran herramienta para aprender. Para los principiantes, como yo, como lo fuimos todos en algún momento. ¿Por qué eliminarlo? ¿Alguien puede indicarme el PEP adecuado? No puedo encontrarlo.

catalesia avatar Feb 22 '13 06:02 catalesia
Aceptado

Algunas medidas de rendimiento, usando timeiten lugar de intentar hacerlo manualmente con time.

Primero, Apple 2.7.2 de 64 bits:

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

Ahora, python.org 3.3.0 de 64 bits:

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

Aparentemente, 3.x rangees realmente un poco más lento que xrange2.x. Y la xrangefunción del OP no tiene nada que ver con eso. (No es sorprendente, ya que no es probable que una llamada única a la __iter__ranura sea visible entre 10000000 de llamadas a cualquier cosa que suceda en el bucle, pero alguien mencionó esta posibilidad).

Pero es sólo un 30% más lento. ¿Cómo es que el OP se volvió 2 veces más lento? Bueno, si repito las mismas pruebas con Python de 32 bits, obtengo 1,58 frente a 3,12. Así que supongo que este es otro de esos casos en los que 3.x se ha optimizado para el rendimiento de 64 bits de maneras que perjudican a los de 32 bits.

pero, realmente importa? Mira esto, con 3.3.0 de 64 bits nuevamente:

In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop

Por lo tanto, construirlo listlleva más del doble de tiempo que toda la iteración.

Y en cuanto a "consume muchos más recursos que Python 2.6+", según mis pruebas, parece que un 3.x rangetiene exactamente el mismo tamaño que un 2.x xrangey, incluso si fuera 10 veces más grande, crearía la lista innecesaria. sigue siendo un problema 10000000 veces mayor que cualquier cosa que la iteración del rango pueda hacer.

¿Y qué pasa con un forbucle explícito en lugar del bucle C interno 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

Por lo tanto, se pierde casi tanto tiempo en la fordeclaración como en el trabajo real de iterar el archivo range.

Si le preocupa optimizar la iteración de un objeto de rango, probablemente esté buscando en el lugar equivocado.


Mientras tanto, sigues preguntando por qué xrangese eliminó, sin importar cuántas veces la gente te diga lo mismo, pero lo repetiré nuevamente: no se eliminó: se le cambió el nombre a range, y la versión 2.x rangees lo que se eliminó.

Aquí hay alguna prueba de que el rangeobjeto 3.3 es un descendiente directo del xrangeobjeto 2.x (y no de la rangefunción 2.x): la fuente de 3.3range y 2.7xrange . Incluso puede ver el historial de cambios (vinculado, creo, al cambio que reemplazó la última instancia de la cadena "xrange" en cualquier parte del archivo).

Entonces, ¿por qué es más lento?

Bueno, por un lado, han agregado muchas características nuevas. Por otro lado, han realizado todo tipo de cambios en todas partes (especialmente dentro de la iteración) que tienen efectos secundarios menores. Y se ha trabajado mucho para optimizar drásticamente varios casos importantes, incluso si a veces pesimiza ligeramente los casos menos importantes. Sume todo esto y no me sorprende que iterar lo rangemás rápido posible ahora sea un poco más lento. Es uno de esos casos menos importantes en los que a nadie le importaría lo suficiente como para centrarse. Es probable que nadie tenga un caso de uso en la vida real en el que esta diferencia de rendimiento sea el punto crítico de su código.

abarnert avatar Feb 22 '2013 00:02 abarnert

El rango de Python3 es el rango x de Python2. No es necesario envolverlo con un iter. Para obtener una lista real en Python3, debe usarlist(range(...))

Si quieres algo que funcione con Python2 y Python3, prueba esto

try:
    xrange
except NameError:
    xrange = range
John La Rooy avatar Feb 22 '2013 01:02 John La Rooy

El tipo de Python 3 rangefunciona igual que el de Python 2 xrange. No estoy seguro de por qué estás viendo una desaceleración, ya que el iterador devuelto por tu xrangefunción es exactamente lo que obtendrías si iteraras rangedirectamente.

No puedo reproducir la desaceleración en mi sistema. Así es como lo probé:

Python 2, con 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, rangees un poquito más rápido:

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

Recientemente aprendí que rangeel tipo de Python 3 tiene otras características interesantes, como la compatibilidad con el corte: range(10,100,2)[5:25:5]is range(20, 60, 10)!

Blckknght avatar Feb 21 '2013 23:02 Blckknght

Una forma de arreglar su código python2 es:

import sys

if sys.version_info >= (3, 0):
    def xrange(*args, **kwargs):
        return iter(range(*args, **kwargs))
andrew pate avatar Jan 22 '2016 15:01 andrew pate