2013-04-19 13 views
5

Was ist der richtige Weg, um mehrere Iterationen über einen Container durchzuführen? Von Python-Dokumentation:Korrekter Weg, um zweimal über eine Liste zu iterieren?

Iterator - Ein Container-Objekt (wie eine Liste) erzeugt ein neues, frisches Iterator jedes Mal, wenn Sie es an die iter() Funktion übergeben oder in einem for-Schleife verwenden. Wenn Sie dies mit einem Iterator versuchen, wird nur das erschöpfte Iterator-Objekt zurückgegeben, das im vorherigen Iterationsdurchgang verwendet wurde, wodurch es wie ein leerer Container erscheint.

Die Absicht des Protokolls ist, dass, sobald die next() - Methode eines Iterators StopIteration auslöst, dies auch bei nachfolgenden Aufrufen der Fall ist. Implementierungen, die dieser Eigenschaft nicht folgen, gelten als fehlerhaft. (Diese Einschränkung wurde in Python 2.3 hinzugefügt, in Python 2.2, verschiedene Iteratoren sind nach dieser Regel gebrochen.)

Wenn ich diesen Code haben:

slist = [1,2,3,4] 
rlist = reversed(slist) 
list(rlist) 
#[4,3,2,1] 
tuple(rlist) 
#() 

Was wäre die einfachste und richtigste Art zweimal über 'rlist' zu iterieren?

+4

Hinweis iterieren müssen, dass Sie nicht Iterieren über eine Liste zweimal - Das ist einfach. Sie durchlaufen tatsächlich zweimal einen ' listreverseterator '>'. – mgilson

Antwort

7
rlist = list(reversed(slist)) 

Dann iterieren Sie so oft wie Sie möchten. Dieser Trick gilt allgemeiner; Wenn Sie mehrmals über einen Iterator iterieren müssen, wandeln Sie ihn in eine Liste um. Hier ist ein Code-Schnipsel, die ich copy-Einfügen in verschiedenen Projekten halten für genau diesen Zweck:

def tosequence(it): 
    """Turn iterable into a sequence, avoiding a copy if possible.""" 
    if not isinstance(it, collections.Sequence): 
     it = list(it) 
    return it 

(Sequence ist die abstrakte Art von Listen, Tupel und viele benutzerdefinierte Liste ähnliche Objekte.)

+0

Warum sich mit dem Test beschäftigen? Ich habe erwartet, dass es keine Kosten für "it = list (it)" gibt, wenn "it" bereits eine Liste ist. Ist das nicht der Fall? –

5

I wouldn ‚t die Liste zweimal gespeichert, wenn Sie es nicht kombinieren kann einmal zu durchlaufen, dann würde ich

slist = [1,2,3,4] 
for element in reversed(slist): 
    print element # do first iteration stuff 
for element in reversed(slist): 
    print element # do second iteration stuff 

man denke nur an die umgekehrt() als ein Reverse-Iterator auf slist einrichten. Das Umgekehrte ist billig. Davon abgesehen, wenn Sie es nur umgekehrt brauchen, würde ich es umgekehrt und es einfach so gespeichert haben.

+1

+1 - Aufruf von 'reversed (slist)' erstellt nur ein neues Iterator-Objekt, das viel billiger ist als 'list (reversed (slist))', das eine Kopie der gesamten Liste erstellt. – Aya

3

Was ist der richtige Weg, um mehrere Iterationen über einen Container durchzuführen?

Tun Sie es einfach zweimal hintereinander. Kein Problem.

Was wäre der einfachste und korrekteste Weg, zweimal über 'rlist' zu iterieren?

See, der Grund, das nicht funktioniert für Sie ist, dass rlistist nicht „ein Container“.

Beachten Sie, wie

list(slist) # another copy of the list 
tuple(slist) # still works! 

So ist die einfache Lösung ist nur sicherzustellen, dass Sie einen tatsächlichen Container von Elementen haben, wenn Sie mehrmals durchlaufen müssen:

rlist = list(reversed(slist)) # we store the result of the first iteration 
# and then that result can be iterated over multiple times. 

Wenn Sie wirklich nicht speichern müssen die Elemente versuchen itertools.tee. Beachten Sie jedoch, dass Sie die Speicherung der Elemente nicht wirklich vermeiden, wenn Sie eine vollständige Iteration ausführen müssen, bevor Sie mit der nächsten beginnen. Im allgemeinen Fall ist Speicher unter diesen Einschränkungen wirklich unvermeidlich.

1

Warum invertieren Sie nicht einfach die ursprüngliche Liste in-place (slist.reverse()), dann durchlaufen Sie es so oft wie Sie möchten, und schließlich wieder umkehren, um die ursprüngliche Liste noch einmal zu erhalten?

Wenn dies nicht für Sie arbeiten, die beste Lösung für in umgekehrter Reihenfolge über die Liste iterieren ist eine neue Reverse-Iterator erstellen jedes Mal, wenn

for _ in xrange(as_many_times_as_i_wish_to_iterate_this_list_in_reverse_order): 
    for x in reversed(slist): 
     do_stuff(x) 
+0

//, Warum verwenden Sie '_' als Variablennamen, hier? Ist das eine Art Konvention? –

+0

Ich (und ich habe gesehen, dass andere Leute dies tun) weisen einer Unterstreichungsvariablen Wegwerfwerte zu. Auf diese Weise weiß ich sofort, wenn ich den Code lese, dass dieser Wert nicht relevant ist. Auf der anderen Seite verwendet die 'gettext'-Bibliothek eine' _() '-Funktion ... also denke ich, dass dies in manchen Kontexten ein schlechter Name für eine Wegwerfvariable ist. –

Verwandte Themen