Beste Methode zum Ändern einer Liste beim Iterieren [duplizieren]

9

Ich habe mehrere Instanzen in einem Python-Skript (v2.6), wo ich eine Liste in-Place ändern muss. Ich muss als Reaktion auf interaktive Eingaben des Benutzers Werte aus der Liste abrufen und möchte die sauberste Methode dafür kennen. Zur Zeit habe ich die sehr schmutzigen Lösungen von a) Setzen von Elementen in der Liste, die ich zu False entfernen und entfernen mit einem Filter oder Liste Verständnis oder b) Erstellen einer völlig neuen Liste während der Schleife, die unnötig zu sein scheint Hinzufügen von Variablen zum Namespace und Aufnehmen von Speicher.

Ein Beispiel für dieses Problem ist wie folgt:

%Vor%

Ich möchte jeden Ordner in der Liste betrachten. Wenn der aktuelle Ordner eine bestimmte Größe unterschreitet, möchte ich den Benutzer fragen, ob er diesen ausschließen möchte. Wenn dies der Fall ist, öffnen Sie den Ordner aus der Liste.

Das Problem mit dieser Routine ist, dass ich, wenn ich über die Liste iteriere, ein unerwartetes Verhalten und eine vorzeitige Beendigung erhalte. Wenn ich eine Kopie durch Slicing iteriere, zieht Pop nicht den richtigen Wert, weil die Indizes verschoben werden und die Problemverbindungen, wenn mehr Elemente gepoppt werden. Ich brauche eine dynamische Listenanpassung dieser Art auch in anderen Bereichen meines Skripts. Gibt es eine saubere Methode für diese Art von Funktionalität?

    
Patrick 24.04.2012, 20:47
quelle

5 Antworten

8

Sie können die Liste rückwärts durchlaufen oder ein Ansichtsobjekt verwenden.

Siehe Ссылка , um eine Liste rückwärts zu durchlaufen. Im Grunde verwenden Sie reversed(yourList) (was passiert, erstellt ein View-Objekt, das rückwärts besucht).

Wenn Sie eine Indexierung benötigen, könnten Sie reversed(enumerate(yourList)) verwenden, aber das würde effektiv eine temporäre Liste im Speicher erstellen, weil enumerate ausgeführt werden müsste, bevor reversed eingreifen könnte. Sie müssen entweder Indexmanipulation durchführen, oder um dies zu tun:

%Vor%

Sogar sauberer: reversed kennt range , also können Sie dies in python3 oder in python2 tun, wenn Sie stattdessen xrange verwenden:

%Vor%

(Beweis: Sie können next(reversed(range(10**10))) ausführen, aber dies wird Ihren Computer zum Absturz bringen, wenn Sie python2 verwenden)

    
ninjagecko 24.04.2012, 20:55
quelle
4

Sie können es rückwärts durchlaufen

Rückwärts:

%Vor%     
jdi 24.04.2012 20:57
quelle
1
  

Zur Zeit habe ich die sehr schmutzigen Lösungen von a) die Elemente in der Liste, die ich entfernen möchte, auf False und entfernen sie mit einem Filter oder Liste Verständnis oder b) erstellen eine völlig neue Liste beim Durchlaufen der Schleife, was scheint unnötig Variablen zum Namespace hinzufügen und Speicher belegen.

Eigentlich ist es nicht diese schmutzige Lösung. Wie lange ist die Liste normalerweise? Selbst die Erstellung der neuen Liste sollte nicht so viel Speicherplatz verbrauchen, da die Liste nur Referenzen enthält.

Sie können auch die while -Schleife in eine Schleife einbinden und für sich selbst aufzählen, indem Sie del lst[n] ausführen, wenn der Benutzer entscheidet (möglicherweise werden die Positionen im Original separat gezählt).

    
pepr 24.04.2012 21:09
quelle
1

OK, ich habe die Lösung gemessen. Die umgekehrten Lösungen sind ungefähr gleich. Die Weiterleitung while Schleife ist etwa 4 mal langsamer. ABER! Die schmutzige Lösung von Patrik ist etwa 80 mal schneller für die Liste von 100.000 zufälligen Ganzzahlen [Fehler in Patrik2 korrigiert] :

%Vor%

Es druckt auf meiner Konsole:

%Vor%

Also, ich habe es mit einer längeren Liste versucht. Wenn man nur doppelt so lange benutzt, macht das einen großen Unterschied (auf meinem alten Computer). Patriks dreckige Lösung verhält sich sehr gut. Es ist etwa 200 mal schneller als die umgekehrten Lösungen:

%Vor%

[Nach ninjageckos Kommentaren hinzugefügt]

Die korrigierte Patrik2-Lösung ist etwa doppelt so schnell wie die 2-stufige Patrick-Lösung.

Um das nicht so häufige Löschen der Elemente zu simulieren, wurde der Test wie if v % 2 != 0: in if v % 100 == 0: geändert. Dann sollten etwa 1% der Artikel gelöscht werden. Es ist offensichtlich, dass es weniger Zeit benötigt. Für 500.000 zufällige Ganzzahlen in der Liste sind die Ergebnisse folgende:

%Vor%

Patricks Lösung ist immer noch etwa 30 mal schneller.

[Hinzugefügt 2012/04/25]

Eine andere Lösung, die an Ort und Stelle arbeitet, sich vorwärts bewegt und das ist so schnell wie Patrick's Lösung. Es bewegt nicht den gesamten Schwanz, wenn das Element gelöscht wird. Stattdessen verschiebt es die gewünschten Elemente an ihre endgültige Position und schneidet dann das nicht verwendete Ende der Liste.

%Vor%

Verglichen mit den obigen Lösungen für v % 100 != 0 :

%Vor%     
pepr 24.04.2012 22:53
quelle
0

Der beste Weg, dies zu handhaben, ist die "Python" -Methode, um Ihre Liste zu überfliegen und eine neue Liste zu erstellen, die nur die gewünschten Ordner enthält. Hier ist, wie ich es tun würde:

%Vor%

Wenn Ihre Liste wirklich groß ist, müssen Sie sich vielleicht Gedanken über die Leistung dieser Lösung machen und schmutzige Tricks verwenden. Aber wenn Ihre Liste so groß ist, könnte es verrückt sein, wenn ein Mensch Ja / Nein-Fragen zu allen Dateien beantwortet, die angezeigt werden könnten.

Ist die Leistung ein tatsächliches Problem oder nur eine Art von quälender Sorge? Weil ich mir ziemlich sicher bin, dass der obige Code schnell genug für den praktischen Gebrauch ist, und dass er leichter zu verstehen und zu modifizieren ist als kniffliger Code.

EDIT: @jdi vorgeschlagen, in den Kommentaren mit itertools.ifilter() oder filter()

Ich habe getestet, und das sollte eigentlich schneller sein als das, was ich oben gezeigt habe:

%Vor%

Ich habe den Benchmark-Code von @pep kopiert und die Lösung mit filter() getestet, wie hier gezeigt. Es war insgesamt zweitschnellster, nur Patrik2 war schneller. Patrik2 war doppelt so schnell, aber wiederum ist jeder Datensatz, der klein genug ist, dass es praktisch ist, dass ein Mensch Ja / Nein-Fragen beantwortet, wahrscheinlich klein genug ist, dass ein Faktor zwei nicht so wichtig ist.

EDIT: Nur zum Spaß ging ich weiter und schrieb eine Version, die ein reines Listenverständnis ist. Es hat einen einzelnen auszuwertenden Ausdruck, keinen Python-Funktionsaufruf.

%Vor%

Ick! Ich mache viel lieber eine Funktion.

    
steveha 24.04.2012 23:37
quelle

Tags und Links