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

218
demandé sur Air 2013-02-22 03:36:54

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.

143
répondu abarnert 2013-02-22 00:30:29

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
119
répondu John La Rooy 2013-02-22 01:09:29
Le type

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

13
répondu Blckknght 2013-02-22 00:01:17

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
répondu andrew pate 2016-01-22 15:18:03

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.

0
répondu Michel Fernandes 2018-04-07 01:52:23

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.

-2
répondu dmitriy 2015-12-27 10:46:21