Quelle est la différence entre les fonctions range et xrange dans Python 2.X?

719

Apparemment, xrange est plus rapide, mais je ne sais pas pourquoi il est plus rapide (et aucune preuve en plus de l'anecdotique à ce jour qu'il est plus rapide) ou ce qui est différent à propos

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

Réponses:

817

En Python 2.x:

  • rangecrée une liste, donc si vous le faites, range(1, 10000000)il crée une liste en mémoire avec des 9999999éléments.

  • xrange est un objet séquence qui évalue paresseusement.

En Python 3, rangefait l'équivalent de python xrange, et pour obtenir la liste, vous devez utiliser list(range(...)).

Charles
la source
65
xrange n'est pas exactement un générateur mais il évalue paresseusement et agit comme un générateur.
Vaibhav Mishra
47
xrange(x).__iter__()est un générateur.
augustomen
34
Pourquoi ont-ils fait xrange, plutôt que de rendre la gamme paresseuse?
Rob Grant
22
@RobertGrant, ils l'ont fait. En Python 3. (Ils ne pouvaient pas faire cela dans la ligne Python 2.x, car toutes les modifications doivent être rétrocompatibles.)
Paul Draper
12
@Ratul signifie que chacun iest évalué à la demande plutôt qu'à l'initialisation.
Onilol
223

range crée une liste, donc si vous le faites, range(1, 10000000)il crée une liste en mémoire avec des 9999999éléments.

xrange est un générateur, c'est donc un objet séquence qui est évalué paresseusement.

C'est vrai, mais en Python 3, .range()sera implémenté par Python 2 .xrange(). Si vous devez réellement générer la liste, vous devrez faire:

list(range(1,100))
Corey
la source
3
Je ne vois pas que ce soit un énorme problème (en ce qui concerne la rupture des applications existantes) car la plage était principalement destinée à générer des index à utiliser dans les boucles pour "pour i dans la plage (1, 10):"
Benjamin Autin
10
+1 Merci pour cette réponse, les informations sur le remplacement de Python 3 par xrange sont très utiles. J'ai en fait dit à quelqu'un d'utiliser xrange à la place ou range et ils ont dit que cela n'avait pas d'importance en python 3, alors j'ai recherché plus d'informations sur google et cette réponse est apparue :)
Cervo
Quel est le problème avec appeler xrangeun générateur? Il s'agit d'une fonction contenant une yieldinstruction et, selon le glossaire, ces fonctions sont appelées générateurs.
lumière d'hiver
@winterlight, pensez que le terme correct est itérateur. Les générateurs devraient également pouvoir recevoir.
McSinyx
112

N'oubliez pas, utilisez le timeitmodule pour tester lequel des petits extraits de code est plus rapide!

$ 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

Personnellement, j'utilise toujours .range(), sauf si j'ai affaire à des listes vraiment énormes - comme vous pouvez le voir, en termes de temps, pour une liste d'un million d'entrées, la surcharge supplémentaire n'est que de 0,04 seconde. Et comme Corey le fait remarquer, dans Python 3.0 .xrange()disparaîtra et .range()vous donnera quand même un bon comportement d'itérateur.

John Fouhy
la source
12
+1 pour timeit exemple. Remarque: pour exécuter dans Windows cmd, il est nécessaire d'utiliser des guillemets doubles, c'est-à-dire ". Donc, le code serapython -m timeit "for i in xrange(1000000):" " pass"
traquer le
10
Le principal avantage de xrange est la mémoire, pas le temps.
endolith
3
+1 pour la réponse pratique: utilisez la plage sauf si elle est énorme . BTW ils sont conceptuellement identiques, correct? Curieusement, aucune réponse ne le dit clairement.
Bob Stein
6
Si xrange est plus rapide et ne prend pas de mémoire, pourquoi utiliser range?
Austin Mohr
8
Je suis généralement d'accord avec votre affirmation, mais votre évaluation est erronée: ce the extra overhead is only 0.04 secondsn'est pas la bonne façon de la voir, elle (90.5-51.1)/51.1 = 1.771 times slowerest correcte car elle indique que si c'est la boucle principale de votre programme, elle peut potentiellement la goulot d'étranglement. Cependant, si c'est une petite partie, alors 1,77x n'est pas beaucoup.
chacham15
65

xrangestocke uniquement les paramètres de plage et génère les nombres à la demande. Cependant, l'implémentation C de Python limite actuellement ses arguments aux longs C:

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]

Notez que dans Python 3.0 il y en a seulement rangeet il se comporte comme le 2.x xrangemais sans les limitations sur les points de fin minimum et maximum.

efotinis
la source
39

xrange renvoie un itérateur et ne conserve qu'un seul numéro en mémoire à la fois. range conserve la liste complète des nombres en mémoire.

Ben Hoffstein
la source
9
xrangene renvoie pas d'itérateur.
abarnert
and only keeps one number in memory at a timeet où les autres sont placés, veuillez me guider ..
SIslam
5
@SIslam S'il connaît le début, la fin et le courant, il peut calculer le suivant, un à la fois.
Justin Meiners
30

Passez un peu de temps avec la référence de la bibliothèque . Plus vous le connaissez, plus vite vous pouvez trouver des réponses à des questions comme celle-ci. Les premiers chapitres sur les objets et types intégrés sont particulièrement importants.

L'avantage du type xrange est qu'un objet xrange prendra toujours la même quantité de mémoire, quelle que soit la taille de la plage qu'il représente. Il n'y a aucun avantage de performance cohérent.

Une autre façon de trouver des informations rapides sur une construction Python est la docstring et la fonction d'aide:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)
Antti Rasinen
la source
1
La bibliothèque est bonne mais ce n'est pas toujours si facile d'obtenir la réponse à la question que vous vous posez.
Teifion
2
Accédez à la référence de la bibliothèque, appuyez sur ctrl + f, recherchez la plage et vous obtiendrez deux résultats. Ce n'est pas beaucoup d'efforts pour trouver la réponse à cette question.
David Locke
1
La référence de bibliothèque ne fonctionne pas. Pouvez-vous le mettre à jour?
mk ..
14

Je suis choqué que personne ne lise le doc :

Cette fonction est très similaire à range(), mais renvoie un xrangeobjet au lieu d'une liste. Il s'agit d'un type de séquence opaque qui donne les mêmes valeurs que la liste correspondante, sans les stocker toutes simultanément. L'avantage de xrange()over range()est minime (car il faut xrange()toujours créer les valeurs quand on les demande) sauf lorsqu'une très grande plage est utilisée sur une machine affamée de mémoire ou lorsque tous les éléments de la plage ne sont jamais utilisés (comme lorsque la boucle est se termine généralement par break).

Kishor Pawar
la source
13

range crée une liste, donc si vous faites range (1, 10000000) il crée une liste en mémoire avec 10000000 éléments. xrange est un générateur, il est donc évalué paresseusement.

Cela vous apporte deux avantages:

  1. Vous pouvez parcourir des listes plus longues sans obtenir de MemoryError.
  2. Comme il résout chaque nombre paresseusement, si vous arrêtez l'itération tôt, vous ne perdrez pas de temps à créer la liste entière.
Lucas S.
la source
12

Vous trouverez l'avantage de xrangeover rangedans cet exemple simple:

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

L'exemple ci-dessus ne reflète rien de mieux en cas de xrange.

Maintenant, regardez le cas suivant où rangeest vraiment très lent par rapport à 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

Avec range, il crée déjà une liste de 0 à 100000000 (chronophage), mais xrangeest un générateur et il ne génère que des nombres en fonction du besoin, c'est-à-dire si l'itération se poursuit.

En Python-3, l'implémentation de la rangefonctionnalité est la même que celle de xrangePython-2, alors qu'elles ont été supprimées xrangeen Python-3

Codage heureux !!

User_Targaryen
la source
11

C'est pour des raisons d'optimisation.

range () créera une liste de valeurs du début à la fin (0 .. 20 dans votre exemple). Cela deviendra une opération coûteuse sur de très grandes gammes.

xrange () d'autre part est beaucoup plus optimisé. il ne calculera la valeur suivante qu'en cas de besoin (via un objet de séquence xrange) et ne créera pas une liste de toutes les valeurs comme le fait range ().

QAZ
la source
9

range(x,y)renvoie une liste de chaque nombre entre x et y si vous utilisez une forboucle, puis rangeest plus lent. En fait, rangea une plus grande plage d'index. range(x.y)imprimera une liste de tous les nombres entre x et y

xrange(x,y)renvoie xrange(x,y)mais si vous avez utilisé une forboucle, alors xrangec'est plus rapide. xrangea une plage d'index plus petite. xrangeimprimera non seulement, xrange(x,y)mais il conservera tous les numéros qui s'y trouvent.

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

Si vous utilisez une forboucle, cela fonctionnerait

[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

Il n'y a pas beaucoup de différence lors de l'utilisation de boucles, bien qu'il y ait une différence lors de l'impression!

Supercolbat
la source
8

range (): range (1, 10) renvoie une liste de 1 à 10 chiffres et conserve 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 le bouclage, c'est légèrement plus rapide que range () et plus efficace en mémoire. xrange () objet comme un itérateur et génère les nombres à la demande. (Évaluation paresseuse)

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
la source
6

Certaines des autres réponses mentionnent que Python 3 a éliminé 2.x rangeet renommé 2.x xrangeen range. Cependant, à moins que vous n'utilisiez 3.0 ou 3.1 (ce que personne ne devrait être), c'est en fait un type quelque peu différent.

Comme le disent les documents 3.1 :

Les objets de plage ont très peu de comportement: ils ne prennent en charge que l'indexation, l'itération et la lenfonction.

Cependant, dans 3.2+, rangeest une séquence complète - il prend en charge les tranches étendues, et toutes les méthodes de collections.abc.Sequenceavec la même sémantique que a list. *

Et, au moins dans CPython et PyPy (les deux implémentations 3.2+ qui existent actuellement), il a également mises en œuvre à temps constant des indexet countméthodes et l' inopérateur (aussi longtemps que vous passez seulement elle entiers). Cela signifie que l'écriture 123456 in rest raisonnable en 3.2+, alors qu'en 2.7 ou 3.1 ce serait une idée horrible.


* Le fait que les issubclass(xrange, collections.Sequence)retours Trueen 2.6-2.7 et 3.0-3.1 est un bug qui a été corrigé dans 3.2 et non rétroporté.

abarnert
la source
6

En python 2.x

range (x) renvoie une liste, qui est créée en mémoire avec x éléments.

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

xrange (x) renvoie un objet xrange qui est un générateur obj qui génère les nombres à la demande. ils sont calculés pendant for-loop (Lazy Evaluation).

Pour le bouclage, c'est légèrement plus rapide que range () et plus efficace en mémoire.

>>> b = xrange(5)
>>> b
xrange(5)
Siyaram Malav
la source
xrange()n'est pas un générateur. xrange(n).__ iter __ () `est.
th3an0maly
5

Lors du test de gamme contre xrange dans une boucle (je sais que je devrais utiliser timeit , mais cela a été rapidement piraté depuis la mémoire en utilisant un exemple de compréhension de liste simple), j'ai trouvé ce qui suit:

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)

qui donne:

$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?

Ou, en utilisant xrange dans la boucle 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

Mes extraits de code sont-ils correctement testés? Des commentaires sur l'instance plus lente de xrange? Ou un meilleur exemple :-)

Dave Everitt
la source
2
Exécuter une référence comme celle-ci, une fois, ne fournit pas de résultats de synchronisation exacts. Il y a toujours un écart .. Il peut s'agir soit de GC, soit d'un autre processus volant le CPU ... n'importe quoi. C'est pourquoi les benchmarks sont généralement exécutés 10-100-1000 -...
Vajk Hermecz
ceci est juste une impression hâtive d'extrait - je l'ai exécuté plusieurs fois, mais seulement jusqu'à environ 100, et xrangesemblait légèrement plus rapide, bien qu'avec Python 3 la comparaison soit maintenant redondante.
Dave Everitt
3
C'est pour ça timeit. Il prend soin de fonctionner plusieurs fois, de désactiver le GC, d'utiliser la meilleure horloge au lieu de time, etc.
abarnert
4

xrange () et range () en python fonctionnent de la même manière que pour l'utilisateur, mais la différence vient lorsque nous parlons de la façon dont la mémoire est allouée en utilisant à la fois la fonction.

Lorsque nous utilisons range (), nous allouons de la mémoire à toutes les variables qu'il génère, il n'est donc pas recommandé d'utiliser avec un plus grand no. de variables à générer.

xrange (), quant à lui, ne génère qu'une valeur particulière à la fois et ne peut être utilisé qu'avec la boucle for pour imprimer toutes les valeurs requises.

Lakshaya Maheshwari
la source
3

plage génère la liste entière et la renvoie. xrange ne le fait pas - il génère les numéros de la liste à la demande.

Eddie Deyo
la source
2

xrange utilise un itérateur (génère des valeurs à la volée), range renvoie une liste.

hacama
la source
2

Quelle?
rangerenvoie une liste statique lors de l'exécution.
xrangerenvoie un object(qui agit comme un générateur, bien qu'il n'en soit certainement pas un) à partir duquel les valeurs sont générées au fur et à mesure des besoins.

Quand utiliser quoi?

  • Utilisez-le xrangesi vous voulez générer une liste pour une gamme gigantesque, disons 1 milliard, surtout lorsque vous avez un "système sensible à la mémoire" comme un téléphone portable.
  • À utiliser rangesi vous souhaitez parcourir plusieurs fois la liste.

PS: Python 3.x de rangefonction == Python 2.x xrangefonction.

kmario23
la source
xrangene renvoie pas d'objet générateur.
abarnert
Si je comprends bien, c'est ainsi que cela est expliqué ici (pour Python 2.x): wiki.python.org/moin/Generators
kmario23
Alors le wiki est faux. (Je ne sais pas qui est le "SH" qui a ajouté et signé ce commentaire.) La documentation officielle est exacte; vous pouvez le tester vous-même et voir s'il s'agit d'un générateur ou d'une séquence.
abarnert
D'accord. Mais c'est toujours déroutant après avoir lu ceci: stackoverflow.com/questions/135041/…
kmario23
1
La question amusante est de savoir quoi faire lorsque l'interprète n'est pas d'accord avec les documents officiels, ou avec un autre interprète… Mais heureusement, cela ne revient pas trop souvent…
abarnert
2

Tout le monde l'a beaucoup expliqué. Mais je voulais qu'il le voie par moi-même. J'utilise python3. J'ai donc ouvert le moniteur de ressources (sous Windows!) Et j'ai d'abord exécuté la commande suivante:

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

puis vérifié la modification dans la mémoire «En cours d'utilisation». C'était insignifiant. Ensuite, j'ai exécuté le code suivant:

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

Et il a fallu une grande partie de la mémoire pour l'utiliser, instantanément. Et j'étais convaincu. Vous pouvez l'essayer par vous-même.

Si vous utilisez Python 2X, remplacez 'range ()' par 'xrange ()' dans le premier code et 'list (range ())' par 'range ()'.

ANKUR SATYA
la source
2

À partir des documents d'aide.

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 différence est apparente. En Python 2.x, rangeretourne une liste, xrangeretourne un objet xrange qui est itérable.

Dans Python 3.x, rangedevient xrangePython 2.x et xrangeest supprimé.

Rajendra Uppal
la source
1

Sur une exigence de numérisation / impression d'éléments 0-N, la plage et la plage x fonctionnent comme suit.

range () - crée une nouvelle liste dans la mémoire et prend l'ensemble de 0 à N éléments (totalement N + 1) et les imprime. xrange () - crée une instance d'itérateur qui parcourt les éléments et ne conserve que l'élément actuellement rencontré dans la mémoire, utilisant ainsi la même quantité de mémoire tout le temps.

Dans le cas où l'élément requis se trouve quelque peu au début de la liste, il économise beaucoup de temps et de mémoire.

Quelques doutes
la source
1
xrangene crée pas d'instance d'itérateur. Il crée un xrangeobjet, qui est itérable, mais pas un itérateur - presque (mais pas tout à fait) une séquence, comme une liste.
abarnert
1

Range renvoie une liste tandis que xrange renvoie un objet xrange qui prend la même mémoire quelle que soit la taille de la plage, car dans ce cas, un seul élément est généré et disponible par itération alors qu'en cas d'utilisation de la plage, tous les éléments sont générés en même temps et sont disponibles dans la mémoire.

user299567
la source
1

La différence diminue pour les arguments plus petits à 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

Dans ce cas, xrange(100)c'est seulement environ 20% plus efficace.

Evgeni Sergeev
la source
1

range: -range remplira tout à la fois, ce qui signifie que chaque numéro de la gamme occupera la mémoire.

xrange: -xrange est quelque chose comme un générateur, il apparaît dans l'image lorsque vous voulez la plage de nombres mais vous ne voulez pas qu'ils soient stockés, comme lorsque vous voulez utiliser la mémoire de loop.so de manière efficace.

tejaswini teju
la source
1

De plus, if do list(xrange(...))sera équivalent à range(...).

C'est donc listlent.

Aussi xrangene termine pas vraiment complètement la séquence

Voilà pourquoi ce n'est pas une liste, c'est un xrangeobjet

U10-Forward
la source
1

range() en Python 2.x

Cette fonction est essentiellement l'ancienne range()fonction qui était disponible en Python 2.xet renvoie une instance d'un listobjet qui contient les éléments dans la plage spécifiée.

Cependant, cette implémentation est trop inefficace lorsqu'il s'agit d'initialiser une liste avec une plage de nombres. Par exemple, for i in range(1000000)serait une commande très coûteuse à exécuter, à la fois en termes de mémoire et d'utilisation du temps car elle nécessite le stockage de cette liste dans la mémoire.


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

Python a 3.xintroduit une nouvelle implémentation de range()(alors que la nouvelle implémentation était déjà disponible en Python 2.xvia lexrange() fonction).

Le range()exploite une stratégie connue sous le nom d' évaluation paresseuse. Au lieu de créer une énorme liste d'éléments dans la plage, la nouvelle implémentation introduit la classe range, un objet léger qui représente les éléments requis dans la plage donnée, sans les stocker explicitement en mémoire (cela peut ressembler à des générateurs mais le concept d'évaluation paresseuse est différent).


À titre d'exemple, considérons les éléments suivants:

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

et

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

Voir cet article pour trouver la différence entre la plage et xrange:

Citer:

rangerenvoie exactement ce que vous pensez: une liste d'entiers consécutifs, d'une longueur définie commençant par 0. xrange, cependant, retourne un "objet xrange" , qui agit beaucoup comme un itérateur

Oko
la source
2
Je me rends compte que cela a 5 ans, mais ce message est faux sur presque tout. xrangen'est pas un itérateur. La liste renvoyée par rangeprend en charge l'itération (une liste est à peu près l'exemple prototypique d'un itérable). L'avantage global de xrangen'est pas "minime". Etc.
abarnert