python - name ''xrange'' is not defined
¿Por qué no hay una función xrange en Python3? (6)
Algunas mediciones de rendimiento, utilizando timeit
lugar de intentar hacerlo manualmente con el 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 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
Al parecer, el range
3.x es un poco más lento que el xrange
2.x. Y la función xrange
del OP no tiene nada que ver con eso. (No es sorprendente, ya que una llamada de una sola vez a la ranura __iter__
no es probable que sea visible entre 10000000 llamadas a lo que suceda en el bucle, pero alguien lo mencionó como una posibilidad).
Pero es solo un 30% más lento. ¿Cómo el OP consiguió 2x tan lento? Bueno, si repito las mismas pruebas con Python de 32 bits, obtengo 1.58 frente a 3.12. Entonces, supongo que este es otro de esos casos en los que 3.x se ha optimizado para un rendimiento de 64 bits de manera que daña a 32 bits.
pero, realmente importa? Mira esto, con 3.3.0 64 bits de nuevo:
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, la creación de la list
lleva más del doble 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 range
3.x es exactamente del mismo tamaño que un xrange
2.x, y, aunque fuera 10 veces más grande, la construcción de La lista innecesaria sigue siendo aproximadamente 10000000x más de un problema que cualquier otra cosa que pueda hacer la iteración del rango.
¿Y qué hay de un bucle for
explícito for
lugar del bucle C dentro de 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
Entonces, casi tanto tiempo perdido en la declaración for
como en el trabajo real de iterar el range
.
Si le preocupa optimizar la iteración de un objeto de rango, probablemente esté buscando en el lugar equivocado.
Mientras tanto, sigue preguntando por xrange
se eliminó xrange
, sin importar cuántas veces la gente te diga lo mismo, pero lo repetiré nuevamente: no se eliminó: se cambió su nombre a range
, y el range
2.x es lo que era remoto.
Aquí hay algunas pruebas de que el objeto de range
3.3 es un descendiente directo del objeto xrange
2.x (y no de la función de range
2.x): la fuente del range
3.3 y del range
2.7 . Incluso puede ver el historial de cambios (vinculado, creo, el 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 todo el lugar (especialmente dentro de la iteración) que tienen efectos secundarios menores. Y había habido mucho trabajo para optimizar dramáticamente varios casos importantes, incluso si a veces pesimiza ligeramente los casos menos importantes. Sume todo esto y no me sorprende que iterar un range
más rápido posible ahora sea un poco más lento. Es uno de esos casos menos importantes 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 hotspot en su código.
Recientemente comencé a usar Python3 y es la falta de heridas de rango extraño.
Ejemplo simple:
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é se ha eliminado xrange? Es una gran herramienta para aprender. Para los principiantes, igual que yo, como todos fuimos en algún momento. ¿Por qué quitarlo? Alguien me puede indicar el PEP adecuado, no puedo encontrarlo.
Aclamaciones.
El rango de Python3 es el rango de Python2. No hay necesidad de envolver un iter a su alrededor. Para obtener una lista real en Python3, debe usar la list(range(...))
Si quieres algo que funcione con Python2 y Python3, prueba esto
try:
xrange
except NameError:
xrange = range
El tipo de range
Python 3 funciona igual que el range
Python 2. No estoy seguro de por qué está viendo una desaceleración, ya que el iterador devuelto por su función xrange
es exactamente lo que obtendría si iterara sobre el range
directamente.
No puedo reproducir la ralentización de mi sistema. Así es como lo he probado:
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, con range
es 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
Hace poco me enteré de que el tipo de range
Python 3 tiene otras características interesantes, como la compatibilidad con el corte en rebanadas: el range(10,100,2)[5:25:5]
es el range(15, 60, 10)
!
Una forma de arreglar tu código python2 es:
import sys
if sys.version_info >= (3, 0):
def xrange(*args, **kwargs):
return iter(range(*args, **kwargs))
xrange de Python 2 es un generador e implementa iterador mientras que range es solo una función. En Python3 no sé por qué se dejó caer el rango.
comp: ~ $ python Python 2.7.6 (predeterminado, 22 de junio de 2015, 17:58:13) [GCC 4.8.2] en 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
Con el número de 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 (predeterminado, 14 de octubre de 2015, 20:28:29) [GCC 4.8.4] en 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
Con timeit number = 1,2,3,4 param trabaja de forma rápida y lineal:
>>> 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
Así que parece que si medimos 1 ciclo de bucle en ejecución como timeit.timeit ("[x para x en el rango (1000000) si x% 4]", número = 1) (como en realidad usamos en código real) python3 funciona lo suficientemente rápido, pero en ciclos repetidos, python 2 xrange () gana en velocidad contra el rango () de python 3.