Quelle est la différence entre les fonctions range et xrange dans Python 2.X?
-
01-07-2019 - |
Question
Apparemment, xrange est plus rapide mais je ne sais pas pourquoi c'est plus rapide (et aucune preuve à part l'anecdote jusqu'à présent), ni ce qui est différent à propos de
for i in range(0, 20):
for i in xrange(0, 20):
La solution
range crée une liste. Ainsi, si vous faites range (1, 10000000)
, il crée une liste en mémoire avec des éléments 9999999
.
xrange
est un objet séquence qui évalue la paresse.
Il convient d'ajouter l'astuce de @ Thiago selon laquelle, dans python3, range correspond à l'équivalent de xrange de python
Autres conseils
range crée une liste. Ainsi, si vous faites
range (1, 10000000)
, il crée une liste en mémoire avec des éléments9999999
.
xrange
est un générateur, ilest donc un objet séquenceest unqui évalue la paresse.
Cela est vrai, mais dans Python 3, .range ()
sera implémenté par Python 2 .xrange ()
. Si vous devez réellement générer la liste, vous devrez:
list(range(1,100))
N'oubliez pas, utilisez le module timeit
pour tester lequel des petits extraits de code est le 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 s’il s’agit de vraiment de très grandes listes - comme vous pouvez le voir, en termes de temps, pour une liste de 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 beau comportement d'itérateur.
xrange
ne stocke que les paramètres de plage et génère les nombres à la demande. Cependant, l’implémentation C de Python limite actuellement ses arguments à C longs:
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 n’existe que la plage
et se comporte comme le 2.x xrange
mais sans les limitations relatives aux extrémités minimales et maximales.
xrange retourne un itérateur et ne garde qu'un seul numéro en mémoire à la fois. gamme conserve la liste complète des numéros en mémoire.
Passez un peu de temps avec la référence de la bibliothèque . Plus vous maîtriserez le sujet, plus vous pourrez trouver rapidement des réponses à de telles questions. Les premiers chapitres sur les objets et les types intégrés sont particulièrement importants.
L’avantage du type xrange est qu’un objet xrange sera toujours prenez la même quantité de mémoire, quelle que soit la taille de la plage qu’elle représente. Il n’existe aucun avantage en termes de performances.
Un autre moyen 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)
range crée une liste. Ainsi, si vous rangez (1, 10000000), il crée une liste en mémoire avec 10000000 éléments. xrange est un générateur, il évalue donc paresseusement.
Cela vous apporte deux avantages:
- Vous pouvez parcourir des listes plus longues sans obtenir un
MemoryError
. - Comme il résout chaque numéro paresseusement, si vous arrêtez l'itération plus tôt, vous ne perdrez pas de temps à créer la liste complète.
Je suis choqué que personne ne lise doc :
Cette fonction est très similaire à
range ()
, mais renvoie un objetxrange
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 en même temps. L’avantage dexrange ()
par rapport àrange ()
est minimal (carxrange ()
doit encore créer les valeurs qui lui sont demandées) sauf lorsqu'une très grande plage est utilisée sur une machine en manque de mémoire ou lorsque tous les éléments de la plage ne sont jamais utilisés (comme lorsque la boucle se termine généralement parbreak
).
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 () est en revanche beaucoup plus optimisé. il ne calculera la valeur suivante que si nécessaire (via un objet séquence xrange) et ne crée pas une liste de toutes les valeurs comme range () le fait.
Vous trouverez l'avantage de xrange
sur plage
dans 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 bien meilleur en cas de xrange
.
Examinons maintenant le cas suivant où plage
est vraiment très lent, comparé à 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 plage
, il crée déjà une liste de 0 à 100000000 (fastidieux), mais xrange
est un générateur et génère uniquement des nombres en fonction du besoin, que est, si l'itération continue.
En Python-3, la mise en oeuvre de la fonctionnalité range
est identique à celle de xrange
en Python-2, alors qu'ils ont supprimé xrange
en Python-3
Bon codage !!
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 le bouclage, 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__
xrange([start,] stop[, step]) -> xrange object
plage (x, y)
renvoie une liste de chaque nombre compris entre x et y si vous utilisez une boucle pour
, puis plage
est plus lent. En fait, range
a une plage d'index plus grande. range (x.y)
affichera une liste de tous les nombres compris entre x et y
xrange (x, y)
renvoie xrange (x, y)
mais si vous avez utilisé une boucle pour
, alors xrange
est plus rapide. xrange
a une plage d'index plus petite. xrange
n'imprimera pas seulement xrange (x, y)
, mais conservera tous les nombres qu'il contient.
[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 boucle pour
, 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’ya pas beaucoup de différence lorsqu’on utilise des boucles, bien qu’il y ait une différence lorsqu’on l’imprime!
En python 2.x
range (x) renvoie une liste 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 la boucle for (Évaluation paresseuse).
Pour les boucles, cela est légèrement plus rapide que range () et utilise plus efficacement la mémoire.
>>> b = xrange(5)
>>> b
xrange(5)
Lorsque vous testez la portée de xrange dans une boucle (je sais que je devrais utiliser timeit , mais cela a été rapidement piraté de mémoire en utilisant un exemple simple de compréhension de liste) 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
Mon extrait teste-t-il correctement? Des commentaires sur l'instance plus lente de xrange? Ou un meilleur exemple: -)
Certaines autres réponses mentionnent que Python 3 a supprimé la plage
de 2.x et renommé le xrange
de la 2.x en plage
. Cependant, à moins que vous n'utilisiez la version 3.0 ou 3.1 (ce que personne ne devrait être), il s'agit en fait d'un type quelque peu différent.
En tant que les documents 3.1 sont , par exemple:
Les objets Range ont très peu de comportement: ils ne supportent que l'indexation, l'itération et la fonction len
.
Cependant, dans 3.2+, plage
est une séquence complète: il prend en charge les tranches étendues et toutes les méthodes de collections.abc.Sequence
avec la même sémantique qu'une liste
. *
Et, au moins dans CPython et PyPy (les deux seules implémentations 3.2+ existantes), il dispose également d'implémentations à temps constant des méthodes index
et count
et l'opérateur dans
(tant que vous ne lui transmettez que des entiers). Cela signifie que l'écriture de 123456 dans r
est raisonnable dans la version 3.2+, alors que dans la version 2.7 ou 3.1, ce serait une idée horrible.
* Le fait que issubclass (xrange, collections.Sequence)
renvoie True
dans les versions 2.6-2.7 et 3.0-3.1 est un bogue qui a été corrigé dans la version 3.2 et non rétroporté.
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 pour utiliser la fonction.
Lorsque nous utilisons range (), nous allouons de la mémoire pour toutes les variables générées. Il est donc déconseillé de l'utiliser avec un plus grand no. des variables à générer.
xrange (), d’autre part, 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.
range génère la liste complète et la renvoie. xrange ne le fait pas - il génère les nombres dans la liste à la demande.
Lisez le post suivant pour faire la comparaison entre range et xrange avec une analyse graphique.
xrange utilise un itérateur (génère des valeurs à la volée), range renvoie une liste.
Quoi?
range
renvoie une liste statique à l'exécution.
xrange
renvoie un objet
(qui agit comme un générateur, même s'il n'en est certainement pas un), à partir duquel les valeurs sont générées à la demande.
Quand utiliser lequel?
- Utilisez
xrange
si vous souhaitez générer une liste pour une étendue gigantesque, disons 1 milliard, en particulier lorsque vous disposez d'un "système sensible à la mémoire". comme un téléphone portable. - Utilisez
plage
si vous souhaitez parcourir plusieurs fois la liste.
PS: Fonction de
de Python 2.x . de Python 3.x
== Fonction
Si vous souhaitez numériser ou imprimer des éléments 0-N, range et xrange fonctionnent comme suit.
range () - crée une nouvelle liste dans la mémoire et prend l’ensemble des éléments 0 à N (totalement N + 1) et les imprime. xrange () - crée une instance d'itérateur qui balaye les éléments et ne garde que l'élément rencontré dans la mémoire, utilisant ainsi la même quantité de mémoire tout le temps.
Si l'élément requis se situe un peu en début de liste, il économisera beaucoup de temps et de mémoire.
La différence diminue pour les arguments plus petits dans la plage (..)
/ 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)
n'est que d'environ 20% plus efficace.
Tout le monde l’a beaucoup expliqué. Mais je voulais le voir par moi-même. J'utilise python3. J'ai donc ouvert le moniteur de ressources (sous Windows!) Et d'abord exécuté la commande suivante:
a=0
for i in range(1,100000):
a=a+i
et ensuite coché 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 vous-même.
Si vous utilisez Python 2X, remplacez "range ()" par "xrange ()" dans le premier code et "list (range ())" par "range ()".
Range renvoie une liste , tandis que xrange renvoie un objet xrange qui utilise la même mémoire quelle que soit la plage. taille, comme dans ce cas, un seul élément est généré et disponible par itération, tandis qu’en cas d’utilisation de range, tous les éléments sont générés en même temps et sont disponibles dans la mémoire.
range: -range va tout peupler en même temps. Cela signifie que chaque numéro de la plage occupera la mémoire.
xrange: -xrange est quelque chose comme un générateur, il entrera en image quand vous voulez la plage de nombres mais vous ne voulez pas qu'ils soient stockés, comme quand vous voulez utiliser dans pour loop.so une mémoire efficace.
À partir de la documentation 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. Dans Python 2.x, range
renvoie une liste, xrange
renvoie un objet xrange qui est itérable.
Dans Python 3.x, la plage
devient xrange
de Python 2.x et xrange
est supprimé.
De plus, si list (xrange (...))
équivaut à plage (...)
.
La liste
est donc lente.
De même, xrange
ne termine vraiment pas complètement la séquence
C’est pourquoi ce n’est pas une liste, c’est un objet xrange
Consultez cette post pour trouver la différence entre range et xrange. :
Pour citer:
plage
renvoie exactement ce que vous pensez: une liste de entiers, d'une longueur définie commençant par 0.xrange
, cependant, retourne un "objet xrange" , qui agit beaucoup comme un itérateur