¿Debería siempre favorecer a xrange () sobre el rango ()?

¿Por qué o por qué no?

Para el rendimiento, especialmente cuando estás iterando en un rango grande, xrange() generalmente es mejor. Sin embargo, todavía hay algunos casos por los que puede preferir range() :

  • En Python 3, range() hace lo que xrange() solía hacer y xrange() no existe. Si desea escribir código que se ejecute tanto en Python 2 como en Python 3, no puede usar xrange() .

  • range() puede ser más rápido en algunos casos, por ejemplo. si iterando sobre la misma secuencia varias veces. xrange() tiene que reconstruir el objeto entero cada vez, pero el range() tendrá objetos enteros reales. (Sin embargo, siempre funcionará peor en términos de memoria)

  • xrange() no es utilizable en todos los casos donde se necesita una lista real. Por ejemplo, no admite segmentos, o cualquier método de lista.

[Editar] Hay un par de publicaciones que mencionan cómo se actualizará el range() mediante la herramienta 2to3. Para el registro, aquí está el resultado de ejecutar la herramienta en algunos usos de muestra de range() y xrange()

 RefactoringTool: Skipping implicit fixer: buffer RefactoringTool: Skipping implicit fixer: idioms RefactoringTool: Skipping implicit fixer: ws_comma --- range_test.py (original) +++ range_test.py (refactored) @@ -1,7 +1,7 @@ for x in range(20): - a=range(20) + a=list(range(20)) b=list(range(20)) c=[x for x in range(20)] d=(x for x in range(20)) - e=xrange(20) + e=range(20) 

Como puede ver, cuando se usa en un bucle for o comprensión, o cuando ya está envuelto con list (), el rango se mantiene sin cambios.

No, ambos tienen sus usos:

Use xrange() al iterar, ya que ahorra memoria. Decir:

 for x in xrange(1, one_zillion): 

más bien que:

 for x in range(1, one_zillion): 

Por otro lado, use range() si realmente desea una lista de números.

 multiples_of_seven = range(7,100,7) print "Multiples of seven < 100: ", multiples_of_seven 

Debe favorecer el range() sobre xrange() solo cuando necesite una lista real. Por ejemplo, cuando desea modificar la lista devuelta por range() , o cuando desea dividirla. Para la iteración o incluso simplemente la indexación normal, xrange() funcionará bien (y generalmente mucho más eficiente). Hay un punto en el que el range() es un poco más rápido que xrange() para listas muy pequeñas, pero dependiendo de su hardware y otros detalles, el punto de equilibrio puede tener un resultado de 1 o 2; No es algo de qué preocuparse. Prefiero xrange() .

Otra diferencia es que xrange () no puede admitir números más grandes que C ints, por lo que si desea tener un rango utilizando el soporte de números grandes integrado en python, debe usar range ().

 Python 2.7.3 (default, Jul 13 2012, 22:29:01) [GCC 4.7.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> range(123456787676676767676676,123456787676676767676679) [123456787676676767676676L, 123456787676676767676677L, 123456787676676767676678L] >>> xrange(123456787676676767676676,123456787676676767676679) Traceback (most recent call last): File "", line 1, in  OverflowError: Python int too large to convert to C long 

Python 3 no tiene este problema:

 Python 3.2.3 (default, Jul 14 2012, 01:01:48) [GCC 4.7.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> range(123456787676676767676676,123456787676676767676679) range(123456787676676767676676, 123456787676676767676679) 

xrange() es más eficiente porque en lugar de generar una lista de objetos, solo genera un objeto a la vez. En lugar de 100 enteros, y toda su sobrecarga, y la lista para colocarlos, solo tienes un entero a la vez. Generación más rápida, mejor uso de la memoria, código más eficiente.

A menos que específicamente necesite una lista para algo, siempre xrange()

range () devuelve una lista, xrange () devuelve un objeto xrange.

xrange () es un poco más rápido y un poco más eficiente en memoria. Pero la ganancia no es muy grande.

Por supuesto, la memoria extra utilizada por una lista no solo se desperdicia, sino que las listas tienen más funciones (división, repetición, inserción, …). Las diferencias exactas se pueden encontrar en la documentación . No hay una regla dura, usa lo que se necesita.

Python 3.0 aún está en desarrollo, pero el rango IIRC () será muy similar al xrange () de 2.X y la lista (range ()) se puede usar para generar listas.

Solo me gustaría decir que REALMENTE no es tan difícil obtener un objeto xrange con funcionalidad de división e indexación. He escrito un código que funciona bastante bien y es tan rápido como xrange para cuando cuenta (iteraciones).

 from __future__ import division def read_xrange(xrange_object): # returns the xrange object's start, stop, and step start = xrange_object[0] if len(xrange_object) > 1: step = xrange_object[1] - xrange_object[0] else: step = 1 stop = xrange_object[-1] + step return start, stop, step class Xrange(object): ''' creates an xrange-like object that supports slicing and indexing. ex: a = Xrange(20) a.index(10) will work Also a[:5] will return another Xrange object with the specified attributes Also allows for the conversion from an existing xrange object ''' def __init__(self, *inputs): # allow inputs of xrange objects if len(inputs) == 1: test, = inputs if type(test) == xrange: self.xrange = test self.start, self.stop, self.step = read_xrange(test) return # or create one from start, stop, step self.start, self.step = 0, None if len(inputs) == 1: self.stop, = inputs elif len(inputs) == 2: self.start, self.stop = inputs elif len(inputs) == 3: self.start, self.stop, self.step = inputs else: raise ValueError(inputs) self.xrange = xrange(self.start, self.stop, self.step) def __iter__(self): return iter(self.xrange) def __getitem__(self, item): if type(item) is int: if item < 0: item += len(self) return self.xrange[item] if type(item) is slice: # get the indexes, and then convert to the number start, stop, step = item.start, item.stop, item.step start = start if start != None else 0 # convert start = None to start = 0 if start < 0: start += start start = self[start] if start < 0: raise IndexError(item) step = (self.step if self.step != None else 1) * (step if step != None else 1) stop = stop if stop is not None else self.xrange[-1] if stop < 0: stop += stop stop = self[stop] stop = stop if stop > self.stop: raise IndexError if start < self.start: raise IndexError return Xrange(start, stop, step) def index(self, value): error = ValueError('object.index({0}): {0} not in object'.format(value)) index = (value - self.start)/self.step if index % 1 != 0: raise error index = int(index) try: self.xrange[index] except (IndexError, TypeError): raise error return index def __len__(self): return len(self.xrange) 

Honestamente, creo que todo el asunto es un poco tonto y todo el mundo debería hacer todo esto de todos modos ...

Un buen ejemplo dado en el libro: Practical Python Por Magnus Lie Hetland

 >>> zip(range(5), xrange(100000000)) [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)] 

No recomendaría usar el rango en lugar de xrange en el ejemplo anterior, aunque solo se necesitan los primeros cinco números, el rango calcula todos los números y eso puede llevar mucho tiempo. Con xrange, esto no es un problema porque solo calcula los números necesarios.

Sí, leí la respuesta de @Bellian: En python 3, range () es un generador y xrange () no existe.

Ir con rango por estas razones:

1) xrange se irá en nuevas versiones de Python. Esto le da fácil compatibilidad futura.

2) el rango tomará las eficiencias asociadas con xrange.

Bien, todos aquí como una opinión diferente en cuanto a las ventajas y desventajas de xrange versus range. En su mayoría son correctos, xrange es un iterador y el rango se despliega y crea una lista real. Para la mayoría de los casos, realmente no notará una diferencia entre los dos. (Puedes usar el mapa con rango pero no con xrange, pero usa más memoria).

Sin embargo, creo que lo que quieres escuchar es que la opción preferida es xrange. Dado que el rango en Python 3 es un iterador, la herramienta de conversión de código 2to3 convertirá correctamente todos los usos de xrange en rango, y arrojará un error o advertencia para los usos de rango. Si desea asegurarse de convertir fácilmente su código en el futuro, utilizará solo xrange y lista (xrange) cuando esté seguro de que desea una lista. Aprendí esto durante el sprint de CPython en PyCon este año (2008) en Chicago.

  • range() : range(1, 10) devuelve una lista de 1 a 10 números y mantiene la lista completa en la memoria.
  • xrange() : como range() , pero en lugar de devolver una lista, devuelve un objeto que genera los números en el rango a pedido. Para los bucles, esto es ligeramente más rápido que el range() y más eficiente en memoria. xrange() opone a un iterador y genera los números a pedido (Evaluación perezosa).
 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__ Out[3]: xrange([start,] stop[, step]) -> xrange object 

range() hace lo mismo que xrange() usa en Python 3 y no existe el término xrange() en Python 3. range() puede ser más rápido en algunos escenarios si se repite la misma secuencia varias veces. xrange() tiene que reconstruir el objeto entero cada vez, pero el range() tendrá objetos enteros reales.

Mientras xrange es más rápido que el range en la mayoría de las circunstancias, la diferencia en el rendimiento es bastante mínima. El pequeño progtwig a continuación compara la iteración en un range y un xrange :

 import timeit # Try various list sizes. for list_len in [1, 10, 100, 1000, 10000, 100000, 1000000]: # Time doing a range and an xrange. rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n'%list_len, number=1000) xrtime = timeit.timeit('a=0;\nfor n in xrange(%d): a += n'%list_len, number=1000) # Print the result print "Loop list of len %d: range=%.4f, xrange=%.4f"%(list_len, rtime, xrtime) 

Los resultados a continuación muestran que el xrange es realmente más rápido, pero no lo suficiente como para sudar.

 Loop list of len 1: range=0.0003, xrange=0.0003 Loop list of len 10: range=0.0013, xrange=0.0011 Loop list of len 100: range=0.0068, xrange=0.0034 Loop list of len 1000: range=0.0609, xrange=0.0438 Loop list of len 10000: range=0.5527, xrange=0.5266 Loop list of len 100000: range=10.1666, xrange=7.8481 Loop list of len 1000000: range=168.3425, xrange=155.8719 

Por lo tanto, use xrange , pero a menos que esté en un hardware restringido, no se preocupe demasiado por ello.