web-dev-qa-db-fra.com

Devez-vous toujours préférer xrange () à range ()?

Pourquoi ou pourquoi pas?

455
mbac32768

Pour les performances, en particulier lorsque vous parcourez une plage étendue, xrange() est généralement préférable. Cependant, il existe encore quelques cas pour lesquels vous pourriez préférer range():

  • Dans python 3, range() fait ce que xrange() faisait auparavant et xrange() n'existe pas. Si vous voulez écrire du code qui s'exécutera à la fois sur Python 2 et Python 3, vous ne pouvez pas utiliser xrange().

  • range() peut être plus rapide dans certains cas - par exemple. si itérer plusieurs fois sur la même séquence. xrange() doit reconstruire l'objet entier à chaque fois, mais range() aura des objets entiers réels. (Cependant, sa performance sera toujours pire en termes de mémoire)

  • xrange() n'est pas utilisable dans tous les cas où une liste réelle est nécessaire. Par exemple, il ne prend pas en charge les tranches ni les méthodes de liste.

[Modifier] Il y a quelques articles qui mentionnent comment range() sera mis à niveau par l'outil 2to3. Pour mémoire, voici le résultat de l'exécution de l'outil sur quelques exemples d'utilisation de range() et 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)

Comme vous pouvez le constater, lorsqu'il est utilisé dans une boucle for ou une compréhension, ou lorsqu'il est déjà encapsulé avec list (), range reste inchangé.

440
Brian

Non, ils ont tous deux leurs utilisations:

Utilisez xrange() lors de l'itération, car cela économise de la mémoire. Dire:

for x in xrange(1, one_zillion):

plutôt que:

for x in range(1, one_zillion):

D'autre part, utilisez range() si vous voulez réellement une liste de nombres.

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

Vous devez privilégier range() par rapport à xrange() uniquement lorsque vous avez besoin d'une liste réelle. Par exemple, lorsque vous souhaitez modifier la liste renvoyée par range() ou lorsque vous souhaitez la découper en tranches. Pour une itération ou même pour une indexation normale, xrange() fonctionnera correctement (et généralement beaucoup plus efficacement). Il y a un point où range() est un peu plus rapide que xrange() pour de très petites listes, mais en fonction de votre matériel et de divers autres détails, le seuil de rentabilité peut être égal à longueur 1 ou 2; pas de quoi s'inquiéter. Préférez xrange().

42
Thomas Wouters

Une autre différence est que xrange () ne peut pas prendre en charge des nombres plus grands que C ints. Par conséquent, si vous souhaitez utiliser une plage utilisant la prise en charge intégrée des grands nombres de python, vous devez utiliser 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 "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C long

Python 3 n'a pas ce problème:

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)
30
Brian Minton

xrange() est plus efficace car au lieu de générer une liste d'objets, il ne génère qu'un objet à la fois. Au lieu de 100 nombres entiers et de tous leurs frais généraux, ainsi que de la liste pour les insérer, vous ne disposez que d'un nombre entier à la fois. Génération plus rapide, meilleure utilisation de la mémoire, code plus efficace.

Sauf si j'ai spécifiquement besoin d'une liste pour quelque chose, je privilégie toujours xrange()

13
Dan Udey

range () renvoie une liste, xrange () renvoie un objet xrange.

xrange () est un peu plus rapide et utilise un peu plus la mémoire. Mais le gain n'est pas très important.

La mémoire supplémentaire utilisée par une liste n'est bien sûr pas gaspillée, les listes ont davantage de fonctionnalités (découper, répéter, insérer, ...). Les différences exactes peuvent être trouvées dans le documentation . Il n'y a pas de règle osseuse, utilisez ce qui est nécessaire.

Python 3.0 est encore en développement, mais range IIRC () sera très similaire à xrange () de 2.X et list (range ()) pourra être utilisé pour générer des listes.

8
jschultz

Je voudrais juste dire qu’il n’est VRAIMENT pas si difficile d’obtenir un objet xrange avec une fonctionnalité de tranche et d’indexation. J'ai écrit du code qui fonctionne assez bien et qui est aussi rapide que xrange pour quand ça compte (itérations).

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)

Honnêtement, je pense que toute la question est un peu bête et xrange devrait faire tout cela de toute façon ...

5
Garrett Berg

Un bon exemple donné dans le livre: Practical Python Par Magnus Lie Hetland

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

Je ne recommanderais pas d’utiliser range au lieu de xrange dans l’exemple précédent - bien que seuls les cinq premiers nombres soient nécessaires, range calcule tous les nombres, ce qui peut prendre beaucoup de temps. Avec xrange, ce n’est pas un problème car il ne calcule que les nombres nécessaires.

Oui, j'ai lu la réponse de @ Brian: Dans python 3, range () est de toute façon un générateur et xrange () n'existe pas.

4
Grijesh Chauhan

Aller avec gamme pour ces raisons:

1) xrange disparaîtra dans les nouvelles versions de Python. Cela vous donne une compatibilité future facile.

2) gamme prendra sur l'efficacité associée à xrange.

3
torial

Alors que xrange est plus rapide que range dans la plupart des cas, la différence de performances est assez minime. Le petit programme ci-dessous compare l’itération sur une range et une 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)

Les résultats ci-dessous montrent que xrange est certes plus rapide, mais pas suffisant pour faire transpirer.

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

Donc, bien sûr, utilisez xrange, mais à moins que vous ne soyez sur un matériel contraint, ne vous inquiétez pas trop à ce sujet.

2
speedplane

D'accord, tout le monde ici a une opinion différente sur les compromis et les avantages de xrange versus range. Ils sont pour la plupart corrects, xrange est un itérateur, et s'étend et crée une liste réelle. Dans la majorité des cas, vous ne remarquerez pas vraiment de différence entre les deux. (Vous pouvez utiliser map avec range mais pas avec xrange, mais cela utilise plus de mémoire.)

Ce que je pense que vous voulez entendre, c’est que le choix préféré est xrange. Étant donné que range in Python 3 est un itérateur, l'outil de conversion de code 2to3 convertira correctement toutes les utilisations de xrange en range et émettra une erreur ou un avertissement pour les utilisations de range. Si vous voulez être sûr de convertir facilement votre code à l'avenir, vous utiliserez uniquement xrange et list (xrange) lorsque vous êtes sûr de vouloir une liste. J'ai appris cela lors du sprint CPython à PyCon cette année (2008) à Chicago.

2
Douglas Mayle
  • range(): range(1, 10) renvoie une liste de 1 à 10 numéros et conserve la liste entière en mémoire.
  • xrange(): Comme range(), mais au lieu de renvoyer une liste, retourne un objet qui génère les nombres de la plage à la demande. Pour la mise en boucle, cela est légèrement plus rapide que range() et utilise davantage la mémoire. xrange() comme un itérateur et génère les nombres à la demande (Lazy Evaluation).
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() fait la même chose que xrange() que l'on utilisait auparavant dans Python 3 et qu'il n'y a pas de terme xrange() existant dans Python 3. range() peut en réalité être plus rapide dans certains scénarios si vous effectuez plusieurs itérations sur la même séquence. xrange() doit reconstruire l'objet entier à chaque fois, mais range() aura des objets entiers réels.

2
Tushar.PUCSD