Récemment, j'ai commencé à utiliser Python3 et le manque de xrange fait mal.
Exemple simple:
1) Python2:
from time import time as t
def count():
st = t()
[x for x in xrange(10000000) if x%4 == 0]
et = t()
print et-st
count()
2) Python3:
from time import time as t
def xrange(x):
return iter(range(x))
def count():
st = t()
[x for x in xrange(10000000) if x%4 == 0]
et = t()
print (et-st)
count()
Les résultats sont, respectivement:
1) 1.53888392448 2) 3.215819835662842
Pourquoi donc? Je veux dire, pourquoi Xrange a été supprimé? C'est un outil formidable à apprendre. Pour les débutants, tout comme moi, comme nous étions tous à un moment donné. Pourquoi l'enlever? Quelqu'un peut-il m'indiquer le PEP approprié, je ne le trouve pas.
À votre santé.
Quelques mesures de performance, en utilisant timeit
au lieu d'essayer de le faire manuellement avec time
.
Premièrement, Apple 2.7.2 64 bits:
In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.05 s per loop
Maintenant, python.org 3.3.0 64 bits:
In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.32 s per loop
In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.31 s per loop
In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.33 s per loop
Apparemment, 3.x range
est vraiment un peu plus lent que 2.x xrange
. Et la fonction xrange
de l'OP n'a rien à voir avec cela. (Pas étonnant, car un appel unique à l'emplacement __iter__
n'est probablement pas visible parmi 10000 000 appels de tout ce qui se passe dans la boucle, mais quelqu'un l'a évoqué comme possibilité.)
Mais ce n'est que 30% plus lent. Comment l'OP est-il devenu 2x aussi lent? Eh bien, si je répète les mêmes tests avec Python 32 bits, je reçois 1,58 contre 3,12. Je suppose donc que c’est là un autre cas dans lequel la version 3.x a été optimisée pour les performances 64 bits d’une manière qui fait mal à la version 32 bits.
Mais est-ce que c'est vraiment important? Vérifiez ceci avec 3.3.0 64 bits à nouveau:
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop
Ainsi, la construction de list
prend plus de deux fois plus de temps que la totalité de l'itération.
Et quant à "consomme beaucoup plus de ressources que Python 2.6+", d'après mes tests, il semble qu'un 3.x range
a exactement la même taille qu'un 2.x xrange
- et, même s'il était 10 fois plus gros, la construction du La liste inutile est toujours environ 10000000x plus un problème que tout ce que l’itération de plage pourrait faire.
Et qu’en est-il d’une boucle explicite for
au lieu de la boucle C dans deque
?
In [87]: def consume(x):
....: for i in x:
....: pass
In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
1 loops, best of 3: 1.85 s per loop
Donc, presque autant de temps perdu dans la déclaration for
que dans le travail réel d’itération de range
.
Si vous souhaitez optimiser l'itération d'un objet Range, vous êtes probablement au mauvais endroit.
En attendant, vous continuez à demander pourquoi xrange
a été supprimé, peu importe le nombre de fois que des personnes vous disent la même chose, mais je le répète: il n'a pas été supprimé: il a été renommé en range
et le 2.x range
est ce qui était enlevé.
Voici quelques preuves que l'objet 3.3 range
est un descendant direct de l'objet 2.x xrange
(et non de la fonction 2.x range
): la source de 3.3 range
et 2.7 xrange
. Vous pouvez même voir l'historique change (lié, je crois, au changement qui a remplacé la dernière instance de la chaîne "xrange" n'importe où dans le fichier).
Alors, pourquoi est-ce plus lent?
Eh bien, pour un, ils ont ajouté beaucoup de nouvelles fonctionnalités. D'autre part, ils ont apporté toutes sortes de modifications (en particulier à l'intérieur des itérations) qui ont des effets secondaires mineurs. Et il y avait eu beaucoup de travail pour optimiser de façon spectaculaire divers cas importants, même si cela penchait parfois légèrement les cas moins importants. Ajoutez tout cela, et je ne suis pas surpris qu'itérer un range
aussi vite que possible est maintenant un peu plus lent. C'est l'un de ces cas moins importants sur lesquels personne ne se soucierait jamais assez. Il est peu probable que personne ait un cas d'utilisation réel où cette différence de performances constitue le point chaud de son code.
La gamme de Python3 est la gamme x de Python2. Il n'y a pas besoin d'envelopper une itère autour de cela. Pour obtenir une liste réelle dans Python3, vous devez utiliser list(range(...))
.
Si vous voulez quelque chose qui fonctionne avec Python2 et Python3, essayez ceci
try:
xrange
except NameError:
xrange = range
Le type range
de Python 3 fonctionne exactement comme le xrange
de Python 2. Je ne sais pas pourquoi vous constatez un ralentissement, car l'itérateur renvoyé par votre fonction xrange
est exactement ce que vous obtiendriez si vous effectuiez une itération sur range
directement.
Je ne parviens pas à reproduire le ralentissement sur mon système. Voici comment j'ai testé:
Python 2, avec xrange
:
Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
18.631936646865853
Python 3, avec range
est un tout petit peu plus rapide:
Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
17.31399508687869
J'ai récemment appris que le type range
de Python 3 comportait d'autres fonctionnalités intéressantes, telles que la prise en charge du découpage en tranches: range(10,100,2)[5:25:5]
est range(15, 60, 10)
!
xrange de Python 2 est un générateur et implémente l'itérateur alors que range n'est qu'une fonction . En Python3, je ne sais pas pourquoi il a été supprimé de xrange.
Une façon de corriger votre code python2 est:
import sys
if sys.version_info >= (3, 0):
def xrange(*args, **kwargs):
return iter(range(*args, **kwargs))