Pourquoi N'y a-t-il pas de fonction xrange dans Python3?
récemment j'ai commencé à utiliser Python3 et c'est le manque de xrange qui 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? Je pourquoi xrange a été enlevé? C'est un excellent outil pour apprendre. Pour les débutants, tout comme moi, comme si nous étions tous à un moment donné. Pourquoi le supprimer? Quelqu'un peut m'indiquer la bonne PEP, Je ne peux pas la trouver.
santé.
6 réponses
Certaines mesures de performance, à l'aide de timeit
au lieu d'essayer de le faire manuellement avec time
.
tout d'Abord, 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 ça. (Pas surprenant, comme un appel unique à la fente __iter__
n'est pas susceptible d'être visible parmi 10000000 appels à tout ce qui se passe dans la boucle, mais quelqu'un a évoqué cette possibilité.)
mais c'est seulement 30% plus lent. Comment l'opération a-t-elle été aussi lente? Si je répète les mêmes tests avec Python 32 bits, j'obtiens 1,58 contre 3,12. Donc je suppose que c'est encore un autre de ces cas où 3.x a été optimisé pour une performance 64 bits de manière à nuire à 32 bits.
Mais est-ce vraiment important? Check this out, avec 3.3.0 64 bits de 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 du list
prend plus de deux fois plus de temps que l'itération entière.
et quant à" consomme beaucoup plus de ressources que Python 2.6+", de mes tests, il ressemble à un 3.x range
est exactement la même taille qu'un 2.x xrange
-et, même s'il était 10x aussi grand, la construction de la liste inutile est toujours environ 10000000x plus d'un problème que tout ce que l'itération de gamme pourrait faire.
et qu'en est-il d'une boucle explicite for
au lieu de la boucle C à l'intérieur de 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 l'énoncé for
que dans le travail réel d'itération du range
.
si vous craignez d'optimiser l'itération d'un objet range, vous cherchez probablement au mauvais endroit.
en attendant, vous continuez de vous demander pourquoi xrange
a été supprimé, peu importe le nombre de fois où les gens vous disent la même chose, mais je vais le répéter encore: il n'a pas été supprimé: il a été renommé en range
, et le 2.x range
est ce qui a été supprimé.
Voici une preuve que l'objet 3.3 range
est un descendant direct du 2.x xrange
objet (et non du 2.x range
fonction): la source à 3.3 range
et 2.7 xrange
. Vous pouvez même voir le changer l'historique (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 commencer, ils ont ajouté beaucoup de nouvelles fonctionnalités. Pour un autre, ils ont fait toutes sortes de changements partout (en particulier à l'intérieur de l'itération) qui ont des effets secondaires mineurs. Et il y avait eu beaucoup de travail à optimiser de façon spectaculaire divers cas importants, même s'il pessimise parfois légèrement les cas moins importants. Ajoutez tout cela, et je ne suis pas surpris que itérer un range
aussi vite que possible soit maintenant un peu plus lent. C'est une de ces affaires moins importantes sur laquelle personne ne se soucierait assez pour se concentrer. Personne n'est susceptible d'avoir jamais un cas d'utilisation réelle où cette différence de performance est le point chaud dans leur code.
gamme de Python3 est xrange de Python2. Il n'y a pas besoin d'emballer un iter autour. Pour obtenir une liste réelle en 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
de Python 3 range
fonctionne exactement comme le xrange
de Python 2 . Je ne sais pas pourquoi vous voyez un ralentissement, puisque l'itérateur retourné par votre fonction xrange
est exactement ce que vous obtiendriez si vous itériez directement au-dessus de range
.
Je ne suis pas capable de reproduire le ralentissement de 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 a quelques autres fonctionnalités soignées, telles que le support pour le tranchage: range(10,100,2)[5:25:5]
est range(15, 60, 10)
!
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))
xrange de Python 2 est un générateur et implémente iterator alors que la portée n'est qu'une fonction. En Python3 Je ne sais pas pourquoi a été déposé hors du xrange.
ordi:~$ python Python 2.7.6 (par défaut, le 22 Juin 2015, 17:58:13) [GCC 4.8.2] sur linux2
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
5.656799077987671
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
5.579368829727173
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
21.54827117919922
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
22.014557123184204
avec le numéro de timeit=1 param:
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)
0.2245171070098877
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=1)
0.10750913619995117
ordi:~$ python3 Python 3.4.3 (par défaut, 14 Oct. 2015, 20:28:29) [GCC 4.8.4] sur linux
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
9.113872020003328
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
9.07014398300089
avec le nombre de timeit=1,2,3,4 param fonctionne de manière rapide et linéaire:
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)
0.09329321900440846
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=2)
0.18501482300052885
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=3)
0.2703447980020428
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=4)
0.36209142999723554
il semble donc si nous mesurons 1 cycle de boucle en cours d'exécution comme timeit.timeit ("[x pour x dans la gamme(1000000) si x%4]", Nombre=1) (Comme nous l'utilisons en code réel) python3 fonctionne assez rapidement, mais dans les boucles répétées python 2 xrange () gagne en vitesse contre range () de python 3.