web-dev-qa-db-fra.com

Pourquoi n'y a-t-il pas de fonction xrange dans Python3?

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é.

232
catalesia

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.

155
abarnert

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
129
John La Rooy

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)!

14
Blckknght

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.

0
Michel Fernandes

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))
0
andrew pate