Der beste Weg, um eine "umgekehrte" Liste in Python zu erstellen?

89

Was ist in Python der beste Weg, um eine neue Liste zu erstellen, deren Elemente mit denen einer anderen Liste identisch sind, jedoch in umgekehrter Reihenfolge? (Ich möchte die vorhandene Liste nicht ändern.)

Hier ist eine Lösung, die mir eingefallen ist:

new_list = list(reversed(old_list))

Es ist auch möglich, das Duplikat zu duplizieren und old_listdann umzukehren:

new_list = list(old_list) # or `new_list = old_list[:]`
new_list.reverse()

Gibt es eine bessere Option, die ich übersehen habe? Wenn nicht, gibt es einen zwingenden Grund (z. B. Effizienz), einen der oben genannten Ansätze gegenüber dem anderen zu verwenden?

Davidkammern
quelle

Antworten:

204
newlist = oldlist[::-1]

Das [::-1]Schneiden (das meine Frau Anna gerne "den Mars-Smiley" nennt ;-) bedeutet: Schneiden Sie die gesamte Sequenz mit einem Schritt von -1, dh umgekehrt. Es funktioniert für alle Sequenzen.

Beachten Sie, dass dies ( und die von Ihnen erwähnten Alternativen) einer "flachen Kopie" entspricht, dh wenn die Elemente veränderlich sind und Sie Mutatoren aufrufen, befinden sich die Mutationen in den Elementen in der ursprünglichen Liste auch in den Elementen in der umgekehrte Liste und umgekehrt. Wenn Sie dies vermeiden müssen, ist a copy.deepcopy(obwohl immer eine potenziell kostspielige Operation), gefolgt von a .reverse, die einzig gute Option.

Alex Martelli
quelle
Oh mein! Das ist elegant. Bis vor ein paar Tagen war mir nicht klar, dass es möglich ist, beim Schneiden einen "Schritt" einzuschließen. Jetzt frage ich mich, wie ich jemals ohne gekommen bin! Danke, Alex. :)
Davidchambers
1
Vielen Dank auch für die Erwähnung, dass dies eine flache Kopie erzeugt. Das ist alles, was ich brauche, also werde ich meinem Code einen Mars-Smiley hinzufügen.
Davidchambers
13
Dies scheint wirklich viel magischer und viel weniger lesbar als der ursprüngliche Vorschlag list(reversed(oldlist)). Anders als eine kleinere Mikro-Optimierung, gibt es keinen Grund bevorzugen [::-1]zu reversed()?
Brian Campbell
@BrianCampbell: Was ist "Magie" daran? Wenn Sie das Schneiden überhaupt verstehen, ist es absolut sinnvoll. Wenn Sie das Schneiden nicht verstehen, sollten Sie es wirklich ziemlich früh in Ihrer Python-Karriere lernen. Natürlich reversedhat dies einen großen Vorteil, wenn Sie die Liste nicht benötigen, da sie weder Speicher noch Zeit im Voraus verschwendet. Wenn Sie die Liste jedoch benötigen, ist die Verwendung von [::-1]anstelle von list(reversed())analog zu der Verwendung von [listcomp]anstelle von list(genexpr).
Abarnert
10
@abarnert In Code, mit dem ich arbeite, sehe ich so selten das dritte Slice-Argument. Wenn ich eine Verwendung davon sehe, muss ich nachschlagen, was es bedeutet. Sobald ich dies tue, ist es immer noch nicht offensichtlich, dass die Standardstart- und -endwerte vertauscht werden, wenn der Schritt negativ ist. Auf einen kurzen Blick, ohne die Bedeutung des dritten Arguments nachzuschlagen, könnte ich vermuten, dass dies [::-1]bedeutet, das letzte Element einer Liste zu entfernen, anstatt es umzukehren. reversed(list)gibt genau an, was es tut; es formuliert seine Absicht im Sinne von "Explizit ist besser als implizit", "Lesbarkeit zählt" und "Sparse ist besser als dicht".
Brian Campbell
55

Jetzt lass uns timeit. Hinweis: Alex [::-1]ist am schnellsten :)

$ p -m timeit "ol = [1, 2, 3]; nl = list(reversed(ol))"
100000 loops, best of 3: 2.34 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = list(ol); nl.reverse();"
1000000 loops, best of 3: 0.686 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = ol[::-1];"
1000000 loops, best of 3: 0.569 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = [i for i in reversed(ol)];"
1000000 loops, best of 3: 1.48 usec per loop


$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 44.7 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(ol); nl.reverse();"
10000 loops, best of 3: 27.2 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = ol[::-1];"
10000 loops, best of 3: 24.3 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = [i for i in reversed(ol)];"
10000 loops, best of 3: 155 usec per loop

Update: Die von inspectorG4dget vorgeschlagene List Comp-Methode wurde hinzugefügt. Ich werde die Ergebnisse für sich selbst sprechen lassen.

Sam Dolan
quelle
8
Nur eine Anmerkung - dies ist genau für das Erstellen einer umgekehrten Kopierliste, aber umgekehrt ist für die Iteration immer noch effizienter als[::-1]
Tadhg McDonald-Jensen
7

Anpassungen

Es lohnt sich, einen Basis-Benchmark / eine Basisanpassung für die Timeit-Berechnungen von sdolan bereitzustellen, die die Leistung von "umgekehrt" ohne die oft unnötige list()Konvertierung zeigen. Diese list()Operation fügt der Laufzeit weitere 26 usecs hinzu und wird nur für den Fall benötigt, dass ein Iterator nicht akzeptabel ist.

Ergebnisse:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Berechnungen:

# I ran this set of 100000 and came up with 11.2, twice:
python -m timeit "ol = [1, 2, 3]*1000; nl = reversed(ol)"
100000 loops, best of 3: 11.2 usec per loop

# This shows the overhead of list()
python -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 37.1 usec per loop

# This is the result for reverse via -1 step slices
python -m timeit "ol = [1, 2, 3]*1000;nl = ol[::-1]"
10000 loops, best of 3: 23.6 usec per loop

Schlussfolgerungen:

Der Abschluss dieser Tests ist um 12,4 usecs reversed()schneller als der Slice[::-1]

Mekarpeles
quelle
14
reversed () gibt ein Iterator-Objekt zurück, das faul ausgewertet wird. Ich denke, es ist kein fairer Vergleich mit der Slicing-Notation [:: - 1] im Allgemeinen.
irisierender
1
Selbst in einem Fall, in dem ein Iterator direkt verwendet werden kann, wie z. B. ''.join(reversed(['1','2','3'])), ist die Slice-Methode immer noch> 30% schneller.
Dansalmo
1
Kein Wunder, dass Sie bei den ersten beiden Tests das gleiche Ergebnis erzielt haben: Sie sind identisch!
MestreLion
Meine Ergebnisse ähneln eher diesen. ol [:: - 1] dauert ungefähr doppelt so lange wie list (umgekehrt (ol)). Die ol [:: - 1] -Methode benötigt weniger Zeichen zum Schreiben. Die Liste (umgekehrt (ol)) ist jedoch für Anfänger-Python-Programmierer wahrscheinlich besser lesbar und auf meinem Computer schneller.
Dhj