Vor kurzem begann ich mit Python3 und es & #39; s Mangel an xrange schmerzt.
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 ist das so? Ich meine, warum wurde xrange's entfernt? Es ist so ein tolles Werkzeug zum Lernen. Für die Anfänger, so wie ich, wie wir alle irgendwann einmal waren. Warum wurde es entfernt? Kann mir jemand einen Hinweis auf das richtige PEP geben, ich kann es nicht finden.
Prost!
Einige Leistungsmessungen unter Verwendung von timeit
anstelle des Versuchs, sie manuell mit time
durchzuführen.
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
Jetzt, 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
wirklich ein bisschen langsamer als 2.x xrange
. Und die OP's xrange
Funktion hat nichts damit zu tun. (Nicht überraschend, da ein einmaliger Aufruf des __iter__
-Slots wahrscheinlich nicht unter 10000000 Aufrufen von was auch immer in der Schleife passiert, sichtbar ist, aber jemand hat es als Möglichkeit erwähnt).
Aber es ist nur 30% langsamer. Wie ist der OP 2x so langsam geworden? Nun, wenn ich die gleichen Tests mit 32-Bit-Python wiederhole, erhalte ich 1,58 gegenüber 3,12. Ich vermute also, dass es sich hier um einen weiteren Fall handelt, in dem 3.x für 64-Bit-Leistung auf eine Weise optimiert wurde, die 32-Bit schadet.
Aber ist das wirklich wichtig? Sehen Sie sich das an, wieder mit 3.3.0 64-Bit:
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop
Der Aufbau der "Liste" dauert also mehr als doppelt so lange wie die gesamte Iteration.
Und was "verbraucht viel mehr Ressourcen als Python 2.6+" angeht, so sieht es nach meinen Tests so aus, als ob ein 3.x Bereich
genau die gleiche Größe hat wie ein 2.x Bereich
- und selbst wenn er 10x so groß wäre, ist der Aufbau der unnötigen Liste immer noch ein 10000000x größeres Problem als alles, was die Bereichsiteration möglicherweise tun könnte.
Und wie wäre es mit einer expliziten "for"-Schleife anstelle der C-Schleife innerhalb von "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
Es wird also fast genauso viel Zeit mit der "for"-Anweisung verschwendet wie mit der eigentlichen Arbeit der Iteration des "Bereichs".
Wenn Sie sich Sorgen um die Optimierung der Iteration eines Bereichsobjekts machen, suchen Sie wahrscheinlich an der falschen Stelle.
In der Zwischenzeit fragen Sie immer wieder, warum xrange
entfernt wurde, egal wie oft man Ihnen das Gleiche sagt, aber ich werde es noch einmal wiederholen: Es wurde nicht entfernt: Es wurde in "Bereich" umbenannt, und der 2.x-Bereich wurde entfernt.
Hier's einige Beweise, dass das 3.3 range
Objekt ein direkter Nachkomme des 2.x xrange
Objekts ist (und nicht der 2.x range
Funktion): die Quelle zu 3.3 range
und 2.7 xrange
. Sie können sogar die change history sehen (verlinkt mit, ich glaube, der Änderung, die die letzte Instanz der Zeichenkette "xrange" irgendwo in der Datei ersetzt hat).
Warum ist es also langsamer?
Nun, zum einen haben sie eine Menge neuer Funktionen hinzugefügt. Zum anderen wurden überall (vor allem innerhalb der Iteration) alle möglichen Änderungen vorgenommen, die geringfügige Nebeneffekte haben. Und es gab eine Menge Arbeit, um verschiedene wichtige Fälle drastisch zu optimieren, auch wenn dadurch manchmal weniger wichtige Fälle leicht pessimiert werden. Wenn man das alles zusammenzählt, wundert es mich nicht, dass die schnellstmögliche Iteration eines Bereichs jetzt ein bisschen langsamer ist. Es ist einer dieser weniger wichtigen Fälle, die niemanden so sehr interessieren würden, dass er sich darauf konzentrieren würde. Niemand wird wahrscheinlich jemals einen realen Anwendungsfall haben, bei dem dieser Leistungsunterschied der Hotspot in seinem Code ist.
Python3's range is Python2's xrange. Es besteht keine Notwendigkeit, ein iter um ihn herum zu wickeln. Um eine tatsächliche Liste in Python3 zu erhalten, müssen Sie list(range(...))
verwenden.
Wenn Sie etwas wollen, das mit Python2 und Python3 funktioniert, versuchen Sie dies
try:
xrange
except NameError:
xrange = range
Python 3's range
Typ funktioniert genauso wie Python 2's xrange
. Ich bin mir nicht sicher, warum Sie eine Verlangsamung sehen, da der Iterator, der von Ihrer xrange
Funktion zurückgegeben wird, genau das ist, was Sie bekommen würden, wenn Sie direkt über range
iterieren würden.
Ich bin nicht in der Lage, die Verlangsamung auf meinem System zu reproduzieren. Hier ist wie ich getestet habe:
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 kleines 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 vor kurzem gelernt, dass Python 3's range
Typ einige andere nette Eigenschaften hat, wie z.B. die Unterstützung für Slicing: bereich(10,100,2)[5:25:5]
ist bereich(20, 60, 10)
!