¿Cuál es la diferencia entre las funciones de rango y xrange en Python 2.X?

719

Aparentemente, xrange es más rápido, pero no tengo idea de por qué es más rápido (y no hay pruebas además de lo anecdótico hasta ahora que es más rápido) o qué es diferente de eso

for i in range(0, 20):
for i in xrange(0, 20):
Teifion
fuente

Respuestas:

817

En Python 2.x:

  • rangecrea una lista, así que si lo hace range(1, 10000000), crea una lista en la memoria con 9999999elementos.

  • xrange es un objeto de secuencia que se evalúa perezosamente.

En Python 3, rangehace el equivalente de Python xrange, y para obtener la lista, debe usar list(range(...)).

Charles
fuente
65
xrange no es exactamente un generador pero se evalúa perezosamente y actúa como un generador.
Vaibhav Mishra
47
xrange(x).__iter__()Es un generador.
agosto
34
¿Por qué hicieron xrange, en lugar de hacer el rango perezoso?
Rob Grant
22
@RobertGrant, lo hicieron. En Python 3. (No podían hacer eso en la línea Python 2.x, ya que todos los cambios deben ser compatibles con versiones anteriores.)
Paul Draper,
12
@Ratul significa que cada uno ise evalúa a demanda en lugar de en la inicialización.
Onilol
223

range crea una lista, por lo que si lo hace range(1, 10000000), crea una lista en la memoria con 9999999elementos.

xrange es un generador, por lo que es un objeto de secuencia es una que evalúa pereza.

Esto es cierto, pero en Python 3, Python 2 .range()lo implementará .xrange(). Si realmente necesita generar la lista, deberá hacer lo siguiente:

list(range(1,100))
Corey
fuente
3
No veo que sea un gran problema (con respecto a romper las aplicaciones existentes) ya que el rango fue principalmente para generar índices para ser utilizados en bucles como "para i en el rango (1, 10):"
Benjamin Autin
10
+1 Gracias por esta respuesta, la información sobre Python 3 reemplazando range con xrange es muy útil. De hecho, le dije a alguien que usara xrange o range y me dijeron que no importaba en Python 3, así que busqué en Google más información y apareció esta respuesta :)
Cervo
¿Qué hay de malo en llamar a xrangeun generador? Es una función que contiene una yielddeclaración y, según el glosario, tales funciones se denominan generadores.
luz
@winterlight, piensa que el término correcto es iterador. Los generadores también deberían poder recibir.
McSinyx
112

¡Recuerde, use el timeitmódulo para probar cuál de los pequeños fragmentos de código es más rápido!

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

Personalmente, siempre uso .range(), a menos que estuviera lidiando con listas realmente enormes, como puede ver, en cuanto al tiempo, para una lista de un millón de entradas, la sobrecarga adicional es de solo 0.04 segundos. Y como señala Corey, en Python 3.0 .xrange()desaparecerá y .range()le dará un buen comportamiento de iterador de todos modos.

John Fouhy
fuente
12
+1 por ejemplo de tiempo. Nota: para ejecutar en cmd de Windows es necesario usar comillas dobles, es decir, ". Entonces el código serápython -m timeit "for i in xrange(1000000):" " pass"
acechar el
10
El principal beneficio de xrange es la memoria, no el tiempo.
Endolith
3
+1 para la respuesta práctica: use el rango a menos que sea enorme . Por cierto, son conceptualmente idénticos, ¿correcto? Curiosamente, ninguna respuesta explica eso.
Bob Stein
66
Si xrange es más rápido y no acapara la memoria, ¿por qué usar el rango?
Austin Mohr
8
Estoy de acuerdo con su afirmación en general, pero su evaluación es incorrecta: the extra overhead is only 0.04 secondsno es la forma correcta de verla, (90.5-51.1)/51.1 = 1.771 times sloweres correcta porque transmite que si este es el bucle central de su programa, puede potencialmente obstaculizarlo. Sin embargo, si esta es una parte pequeña, 1.77x no es mucho.
chacham15
65

xrangesolo almacena los parámetros de rango y genera los números a pedido. Sin embargo, la implementación en C de Python actualmente restringe sus argumentos a C largos:

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

Tenga en cuenta que en Python 3.0 solo existe rangey se comporta como el 2.x xrangepero sin las limitaciones en los puntos finales mínimos y máximos.

efotinis
fuente
39

xrange devuelve un iterador y solo mantiene un número en la memoria a la vez. range mantiene la lista completa de números en la memoria.

Ben Hoffstein
fuente
99
xrangeno no devolver un iterador.
abarnert
and only keeps one number in memory at a timey donde se colocan el resto por favor guíame ..
SIslam
55
@SIslam Si conoce el inicio, el final y la corriente, puede calcular el siguiente, uno a la vez.
Justin Meiners
30

Pase algún tiempo con la Referencia de la biblioteca . Cuanto más familiarizado esté con él, más rápido podrá encontrar respuestas a preguntas como esta. Especialmente importantes son los primeros capítulos sobre objetos y tipos incorporados.

La ventaja del tipo xrange es que un objeto xrange siempre tendrá la misma cantidad de memoria, sin importar el tamaño del rango que representa. No hay ventajas consistentes de rendimiento.

Otra forma de encontrar información rápida sobre una construcción de Python es la cadena de documentación y la función de ayuda:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)
Antti Rasinen
fuente
1
La biblioteca es buena, pero no siempre es tan fácil obtener la respuesta a la pregunta que tiene.
Teifion
2
Vaya a la referencia de la biblioteca, presione ctrl + f, busque el rango y obtendrá dos resultados. No es mucho esfuerzo encontrar la respuesta a esta pregunta.
David Locke
1
La referencia de la biblioteca no funciona. ¿Puedes por favor actualizarlo?
mk ..
14

Estoy sorprendido de que nadie lea el documento :

Esta función es muy similar a range(), pero devuelve un xrangeobjeto en lugar de una lista. Este es un tipo de secuencia opaca que produce los mismos valores que la lista correspondiente, sin almacenarlos todos simultáneamente. La ventaja de xrange()over range()es mínima (ya que xrange()todavía tiene que crear los valores cuando se los solicite), excepto cuando se usa un rango muy grande en una máquina sin memoria o cuando nunca se usan todos los elementos del rango (como cuando el ciclo está generalmente terminado con break).

Kishor Pawar
fuente
13

range crea una lista, por lo que si hace range (1, 10000000) crea una lista en memoria con 10000000 elementos. xrange es un generador, por lo que evalúa perezosamente.

Esto te trae dos ventajas:

  1. Puede iterar listas más largas sin obtener un MemoryError.
  2. Como resuelve cada número de manera perezosa, si detiene la iteración antes de tiempo, no perderá tiempo creando la lista completa.
Lucas S.
fuente
12

Encontrará la ventaja de xrangemás rangeen este simple ejemplo:

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds

El ejemplo anterior no refleja nada sustancialmente mejor en caso de xrange.

Ahora mira el siguiente caso donde rangees realmente muy lento, en comparación con xrange.

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer() 

print "time taken: ", (t2-t1)  # 2.78506207466 seconds

Con range, ya crea una lista de 0 a 100000000 (consume mucho tiempo), pero xrangees un generador y solo genera números en función de la necesidad, es decir, si la iteración continúa.

En Python-3, la implementación de la rangefuncionalidad es la misma que xrangeen Python-2, mientras que se ha eliminado xrangeen Python-3

Feliz codificación !!

User_Targaryen
fuente
11

Es por razones de optimización.

range () creará una lista de valores de principio a fin (0 .. 20 en su ejemplo). Esto se convertirá en una operación costosa en rangos muy grandes.

xrange () por otro lado está mucho más optimizado. solo calculará el siguiente valor cuando sea necesario (a través de un objeto de secuencia xrange) y no creará una lista de todos los valores como hace range ().

QAZ
fuente
9

range(x,y)devuelve una lista de cada número entre x e y si usa un forbucle, entonces rangees más lento. De hecho, rangetiene un rango de índice más grande. range(x.y)imprimirá una lista de todos los números entre x e y

xrange(x,y)vuelve xrange(x,y)pero si usaste un forbucle, entonces xrangees más rápido. xrangetiene un rango de índice más pequeño. xrangeno solo se imprimirá, xrange(x,y)sino que también conservará todos los números que contiene.

[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)

Si usa un forbucle, entonces funcionaría

[In] for i in range(1,10):
        print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9
[In] for i in xrange(1,10):
         print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9

¡No hay mucha diferencia al usar bucles, aunque hay una diferencia cuando solo se imprime!

Supercolbat
fuente
8

range (): range (1, 10) devuelve una lista de 1 a 10 números y mantiene la lista completa en la memoria.

xrange (): al igual que range (), pero en lugar de devolver una lista, devuelve un objeto que genera los números en el rango a pedido. Para el bucle, esto es ligeramente más rápido que range () y más eficiente en memoria. xrange () objeto como un iterador y genera los números a pedido. (Evaluación diferida)

In [1]: range(1,10)

Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: xrange(10)

Out[2]: xrange(10)

In [3]: print xrange.__doc__

xrange([start,] stop[, step]) -> xrange object
Tushar.PUCSD
fuente
6

Algunas de las otras respuestas mencionan que Python 3 eliminó 2.x's rangey renombró 2.x's xrangea range. Sin embargo, a menos que esté utilizando 3.0 o 3.1 (que nadie debería usar), en realidad es un tipo algo diferente.

Como dicen los documentos 3.1 :

Los objetos de rango tienen muy poco comportamiento: solo admiten indexación, iteración y la lenfunción.

Sin embargo, en 3.2+, range es una secuencia completa: admite cortes extendidos y todos los métodos collections.abc.Sequencecon la misma semántica que a list. * *

Y, al menos en CPython y PyPy (las únicas dos implementaciones 3.2+ que existen actualmente), también tiene implementaciones de tiempo constante de index y los countmétodos y el inoperador (el tiempo que pasa únicamente se enteros). Esto significa que la escritura 123456 in res razonable en 3.2+, mientras que en 2.7 o 3.1 sería una idea horrible.


* El hecho de que issubclass(xrange, collections.Sequence)regrese Trueen 2.6-2.7 y 3.0-3.1 es un error que se corrigió en 3.2 y no se soportó.

abarnert
fuente
6

En python 2.x

range (x) devuelve una lista, que se crea en la memoria con x elementos.

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]

xrange (x) devuelve un objeto xrange que es un obj generador que genera los números a pedido. se calculan durante for-loop (Lazy Evaluation).

Para el bucle, esto es ligeramente más rápido que range () y más eficiente en memoria.

>>> b = xrange(5)
>>> b
xrange(5)
Siyaram Malav
fuente
xrange()No es un generador. xrange(n).__ iter __ () `es.
th3an0maly
5

Al probar el rango contra xrange en un bucle (sé que debería usar timeit , pero esto fue rápidamente pirateado de la memoria usando un simple ejemplo de comprensión de lista) encontré lo siguiente:

import time

for x in range(1, 10):

    t = time.time()
    [v*10 for v in range(1, 10000)]
    print "range:  %.4f" % ((time.time()-t)*100)

    t = time.time()
    [v*10 for v in xrange(1, 10000)]
    print "xrange: %.4f" % ((time.time()-t)*100)

lo que da:

$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?

O, usando xrange en el bucle for:

range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509

¿Mi fragmento se está probando correctamente? ¿Algún comentario sobre la instancia más lenta de xrange? O un mejor ejemplo :-)

Dave Everitt
fuente
2
Ejecutar un punto de referencia como este, una vez, no proporciona resultados exactos de tiempo. Siempre hay una variación ... Podría ser GC u otro proceso que robe la CPU ... cualquier cosa. Es por eso que los puntos de referencia generalmente se ejecutan 10-100-1000 -...
Vajk Hermecz
esto es solo una impresión apresurada del fragmento: lo ejecuté varias veces, pero solo hasta alrededor de 100, y xrangeparecía un poco más rápido, aunque con Python 3 la comparación ahora es redundante.
Dave Everitt
3
Esto es para lo que timeitsirve. Se encarga de ejecutar muchas veces, deshabilitar GC, usar el mejor reloj en lugar de time, etc.
abarnert
4

xrange () y range () en python funcionan de manera similar para el usuario, pero la diferencia viene cuando hablamos de cómo se asigna la memoria al usar ambas funciones.

Cuando usamos range () asignamos memoria para todas las variables que está generando, por lo que no se recomienda usar con no. de variables a generar.

xrange () por otro lado genera solo un valor particular a la vez y solo puede usarse con el bucle for para imprimir todos los valores requeridos.

Lakshaya Maheshwari
fuente
3

range genera la lista completa y la devuelve. xrange no: genera los números en la lista a pedido.

Eddie Deyo
fuente
2

xrange utiliza un iterador (genera valores sobre la marcha), el rango devuelve una lista.

hacama
fuente
2

¿Qué?
rangedevuelve una lista estática en tiempo de ejecución.
xrangedevuelve unobject (que actúa como un generador, aunque ciertamente no es uno) a partir del cual se generan valores cuando sea necesario.

¿Cuándo usar cuál?

  • Utilizar xrange si desea generar una lista para un rango gigantesco, digamos mil millones, especialmente cuando tiene un "sistema sensible a la memoria" como un teléfono celular.
  • Úselo rangesi desea recorrer la lista varias veces.

PD: Python 3.x rangefunción de Python 2.x == xrangefunción.

kmario23
fuente
xrangeno devuelve un objeto generador.
abarnert
Si entiendo correctamente, así es como se explica aquí (para Python 2.x): wiki.python.org/moin/Generators
kmario23
Entonces la wiki está mal. (No sé quién es el "SH" que agregó y firmó ese comentario). La documentación oficial es correcta; puede probarlo usted mismo y ver si es un generador o una secuencia.
abarnert
Okay. Pero sigue siendo confuso después de leer esto: stackoverflow.com/questions/135041/…
kmario23
1
La pregunta diversión es lo que hay que hacer cuando los intérpretes no está de acuerdo con los documentos oficiales, o con un intérprete diferente ... Pero, afortunadamente, que no vienen demasiado a menudo ...
abarnert
2

Todos lo han explicado mucho. Pero quería que lo viera por mí mismo. Yo uso python3. Entonces, abrí el monitor de recursos (¡en Windows!), Y primero ejecuté el siguiente comando primero:

a=0
for i in range(1,100000):
    a=a+i

y luego verificó el cambio en la memoria 'En uso'. Fue insignificante. Luego, ejecuté el siguiente código:

for i in list(range(1,100000)):
    a=a+i

Y tomó una gran parte de la memoria para su uso, al instante. Y estaba convencido. Puedes probarlo por ti mismo.

Si está utilizando Python 2X, reemplace 'range ()' con 'xrange ()' en el primer código y 'list (range ())' con 'range ()'.

ANKUR SATYA
fuente
2

De los documentos de ayuda.

Python 2.7.12

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is 
slightly faster than range() and more memory efficient.

Python 3.5.2

>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

La diferencia es aparente. En Python 2.x, rangedevuelve una lista,xrange devuelve un objeto xrange que es iterable.

En Python 3.x, se rangeconvierte xrangeen Python 2.x, y xrangese elimina.

Rajendra Uppal
fuente
1

En un requisito para escanear / imprimir elementos 0-N, el rango y el rango x funcionan de la siguiente manera.

range (): crea una nueva lista en la memoria y toma todo el 0 a N elementos (totalmente N + 1) y los imprime. xrange (): crea una instancia de iterador que escanea a través de los elementos y mantiene solo el elemento actual encontrado en la memoria, por lo tanto, utiliza la misma cantidad de memoria todo el tiempo.

En caso de que el elemento requerido esté algo al principio de la lista solo entonces ahorra una buena cantidad de tiempo y memoria.

Algunas dudas
fuente
1
xrangeno crea una instancia de iterador. Crea un xrangeobjeto, que es iterable, pero no un iterador, casi (pero no del todo) una secuencia, como una lista.
abarnert
1

El rango devuelve una lista, mientras que xrange devuelve un objeto xrange que toma la misma memoria independientemente del tamaño del rango, ya que en este caso, solo se genera un elemento y está disponible por iteración, mientras que en caso de usar el rango, todos los elementos se generan a la vez y están disponibles en la memoria.

usuario299567
fuente
1

La diferencia disminuye para argumentos más pequeños para range(..)/ xrange(..):

$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop

En este caso xrange(100)es solo un 20% más eficiente.

Evgeni Sergeev
fuente
1

range: -range completará todo a la vez, lo que significa que cada número del rango ocupará la memoria.

xrange: -xrange es algo así como un generador, aparecerá en la imagen cuando desee el rango de números pero no desee que se almacenen, como cuando desea usarlo para loop.so memoria eficiente.

tejaswini teju
fuente
1

Además, si hacer list(xrange(...))será equivalente arange(...) .

Entonces list es lento.

también xrange realmente no termina completamente la secuencia

Por eso no es una lista, es un xrangeobjeto

U10-Adelante
fuente
1

range() en Python 2.x

Esta función es esencialmente la range()función anterior que estaba disponible en Python 2.xy devuelve una instancia de un listobjeto que contiene los elementos en el rango especificado.

Sin embargo, esta implementación es demasiado ineficiente cuando se trata de inicializar una lista con un rango de números. Por ejemplo, for i in range(1000000)sería un comando muy costoso de ejecutar, tanto en términos de memoria como de uso de tiempo, ya que requiere el almacenamiento de esta lista en la memoria.


range()en Python 3.xy xrange()en Python2.x

Python 3.xintrodujo una implementación más nueva de range()(mientras que la implementación más nueva ya estaba disponible en Python a 2.xtravés dexrange() función).

El range()explota una estrategia conocida como evaluación perezosa. En lugar de crear una gran lista de elementos dentro del rango, la implementación más reciente introduce la clase range, un objeto liviano que representa los elementos requeridos en el rango dado, sin almacenarlos explícitamente en la memoria (esto puede sonar como generadores, pero el concepto de evaluación diferida es diferente).


Como ejemplo, considere lo siguiente:

# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>

y

# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>
Giorgos Myrianthous
fuente
-2

Ver esta publicación para encontrar la diferencia entre rango y xrange:

Citar:

rangedevuelve exactamente lo que piensa: una lista de enteros consecutivos, de una longitud definida que comienza con 0. xrange, sin embargo, devuelve un "objeto xrange" , que actúa mucho como un iterador

Oko
fuente
2
Me doy cuenta de que esto tiene 5 años, pero esa publicación está equivocada en casi todo. xrangeNo es un iterador. La lista devuelta por rangeadmite la iteración (una lista es prácticamente el ejemplo prototípico de un iterable). El beneficio general de xrangeno es "mínimo". Y así.
abarnert