it-swarm.com.de

Warum gibt es in Python3 keine Xrange-Funktion?

Vor kurzem habe ich mit Python3 angefangen und es fehlt an Xrange-Verletzungen.

Einfaches Beispiel:

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

Die Ergebnisse sind jeweils:

1) 1.53888392448 2) 3,215819835662842

Warum das? Ich meine, warum wurde Xrange entfernt? Es ist ein großartiges Werkzeug, um es zu lernen. Für die Anfänger genauso wie ich, als wären wir alle irgendwann gewesen. Warum es entfernen? Kann mich jemand auf die richtige PEP verweisen, ich kann sie nicht finden.

Prost.

232
catalesia

Einige Leistungsmessungen verwenden timeit, anstatt zu versuchen, dies manuell mit time zu tun.

Erstens, Apple 2.7.2 64-Bit:

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

Nun, python.org 3.3.0 64-Bit:

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

Offensichtlich ist 3.x range tatsächlich etwas langsamer als 2.x xrange. Und die Funktion xrange des OP hat nichts damit zu tun. (Kein Wunder, da ein einmaliger Aufruf des __iter__-Slots bei 10000000-Aufrufen an das, was in der Schleife passiert, wahrscheinlich nicht sichtbar ist, aber jemand hat es als Möglichkeit angesprochen.)

Aber es ist nur 30% langsamer. Wie wurde das OP 2x so langsam? Wenn ich dieselben Tests mit 32-Bit-Python wiederhole, bekomme ich 1,58 vs. 3,12. Meine Vermutung ist also, dass dies ein weiterer Fall ist, in dem 3.x für 64-Bit-Leistung in einer Weise optimiert wurde, die 32-Bit verletzt.

Aber ist es wirklich wichtig? Überprüfen Sie dies mit 3.3.0 64-Bit noch einmal:

In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop

Das Erstellen von list dauert also mehr als doppelt so lange wie die gesamte Iteration. 

Und was "bei weitem mehr Ressourcen als bei Python 2.6+" anbelangt, sieht es bei meinen Tests so aus, als sei ein 3.x range genau die gleiche Größe wie ein 2.x xrange und sogar wenn Es war 10 mal so groß, das Erstellen der unnötigen Liste ist immer noch etwa 10000000 mal mehr ein Problem als alles, was die Range-Iteration tun könnte.

Und was ist mit einer expliziten for Schleife anstelle der C-Schleife in 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

In der for -Anweisung wurde also fast genauso viel Zeit verschwendet wie in der eigentlichen Arbeit des Iterierens der range.

Wenn Sie befürchten, die Iteration eines Bereichsobjekts zu optimieren, suchen Sie wahrscheinlich am falschen Ort.


In der Zwischenzeit fragen Sie immer wieder, warum xrange entfernt wurde, egal wie oft Sie dasselbe sagen, aber ich wiederhole es noch einmal: Es wurde nicht entfernt: Es wurde in range umbenannt Die 2.x range wurde entfernt.

Hier ist ein Beweis dafür, dass das 3.3 range-Objekt ein direkter Nachkomme des 2.x xrange-Objekts ist (und nicht der 2.x range-Funktion): die Quelle für 3.3 range und 2.7 xrange . Sie können sogar den change history sehen (verknüpft mit meiner Meinung nach die Änderung, die die letzte Instanz des Strings "xrange" an einer beliebigen Stelle in der Datei ersetzt hat).

Warum ist es langsamer?

Zum einen haben sie viele neue Funktionen hinzugefügt. Zum anderen haben sie überall (vor allem in der Iteration) alle möglichen Änderungen vorgenommen, die geringfügige Nebenwirkungen haben. Und es gab eine Menge Arbeit, um verschiedene wichtige Fälle dramatisch zu optimieren, auch wenn dies manchmal weniger wichtige Fälle pessimiert. Addieren Sie das alles zusammen und ich bin nicht überrascht, dass das Durchlaufen eines range so schnell wie möglich jetzt etwas langsamer ist. Es ist einer dieser unwichtigen Fälle, an denen sich niemand jemals genug interessieren würde. Wahrscheinlich wird es niemals einen realen Anwendungsfall geben, bei dem dieser Leistungsunterschied der Hotspot in seinem Code ist.

155
abarnert

Python3s Bereich ist Python2s Xrange. Es ist nicht nötig, einen Iter um ihn zu wickeln. Um eine aktuelle Liste in Python3 zu erhalten, müssen Sie list(range(...)) verwenden.

Wenn Sie etwas möchten, das mit Python2 und Python3 funktioniert, probieren Sie es aus

try:
    xrange
except NameError:
    xrange = range
129
John La Rooy

Der range-Typ von Python 3 funktioniert genauso wie xrange von Python 2. Ich bin nicht sicher, warum Sie eine Verlangsamung sehen, da der von Ihrer xrange-Funktion zurückgegebene Iterator genau das ist, was Sie erhalten würden, wenn Sie range direkt iterieren.

Ich kann die Verlangsamung meines Systems nicht reproduzieren. So habe ich getestet:

Python 2 mit 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, mit range, ist ein bisschen schneller:

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

Ich habe kürzlich erfahren, dass der range-Typ von Python 3 einige weitere nützliche Funktionen hat, z. B. Unterstützung für das Slicing: range(10,100,2)[5:25:5] ist range(15, 60, 10)!

14
Blckknght

xrange von Python 2 ist ein Generator und implementiert einen Iterator, während range nur eine Funktion ist .. In Python3 weiß ich nicht, warum der Xrange-Bereich verlassen wurde.

0

Eine Möglichkeit, Ihren Python2-Code zu korrigieren, ist:

import sys

if sys.version_info >= (3, 0):
    def xrange(*args, **kwargs):
        return iter(range(*args, **kwargs))
0
andrew pate