Question

Pourquoi ou pourquoi pas?

Était-ce utile?

La solution

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

  • En python 3, range () fait ce que xrange () utilisait et xrange () n'existait pas. Si vous souhaitez écrire du code qui s'exécutera à la fois sur Python 2 et Python 3, vous ne pouvez pas utiliser xrange () .

  • range () peut en réalité ê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 en mémoire sera toujours pire)

  • 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] Plusieurs articles mentionnent comment range () sera mis à niveau par l'outil 2to3. Pour l'enregistrement, voici le résultat de l'exécution de l'outil sur quelques exemples d'utilisation de range () et de 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 (), la plage est laissée inchangée.

Autres conseils

Non, ils ont tous deux leur utilité:

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

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

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 simplement 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 à un résultat de longueur 1 ou 2; pas de quoi s'inquiéter. Préférez xrange () .

Une autre différence est que xrange () ne peut pas supporter des nombres plus grands que C ints, donc si vous voulez avoir 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)

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 préfère toujours xrange ()

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 plus de fonctionnalités (découper, répéter, insérer, ...). Les différences exactes se trouvent dans la documentation . Il n’ya 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 ()) peut être utilisé pour générer des listes.

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 il 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 le problème est plutôt idiot et que xrange devrait faire tout cela de toute façon ...

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. 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 vraiment entendre, c’est que le choix préféré est xrange. Comme range dans 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.

  • plage () : plage (1, 10) renvoie une liste de 1 à 10 chiffres & amp; conservez toute la liste en mémoire.
  • xrange () : Comme range () , mais au lieu de renvoyer une liste, retourne un objet qui génère les nombres dans la plage à la demande. Pour les boucles, cela est légèrement plus rapide que range () et utilise plus efficacement 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 () à faire dans Python 3 et qu'il n'y a pas de terme xrange () en Python 3. range () peut dans certains cas être plus rapide 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.

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

Les résultats ci-dessous montrent que xrange est effectivement plus rapide, mais pas suffisant pour faire fondre la sueur.

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

Utilisez donc xrange , mais à moins que vous ne soyez sur un matériel contraint, ne vous inquiétez pas trop à ce sujet.

scroll top