Liste aufzählen und dabei Elemente entfernen?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

In Java ist es manchmal ganz praktisch, dass man mit einem java.util.Iterator das aktuelle Element löschen kann. Wie lautet das passende Sprachmuster in Python? Ich habe zwei Varianten, die ich beide nicht so toll finde. Übersehe ich etwas?

Code: Alles auswählen

for i in reversed(range(len(sequence))):
    if soll_weg(sequence[i]):
        del sequence[i]

for obj in sequence[:]:
    if soll_weg(obj):
        sequence.remove(obj)
Die zweite Variante funktioniert nur wenn alle Objekte bzgl. __eq__ (oder was auch immer `remove()` für den Vergleich heranzieht) unterschiedlich sind. Außerdem kann man IMHO das `[:]` leicht übersehen.

Die unten stehende funktionale Variante suche ich nicht, denn ich will die Variable nicht verändern.

Code: Alles auswählen

kann_bleiben = lambda obj: not soll_weg(obj)
sequence = filter(kann_bleiben, sequence)
Warum gibt es eigentlich nicht auch das Gegenstück zu `filter`? Bei Smalltalk war es immmer praktisch, sowohl #select: als auch #reject: zu haben. Eigentlich suche ich ja #removeAllSuchThat:. Wieso kann keine andere Sprache vernünftig bei Smalltalk abgucken... Grummel.

Stefan
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

list comprehensions gehen auch, die sind ja nur syntaxzucker für filter, map, lambda

Code: Alles auswählen

sequence = [item for item in sequence if not soll_weg(item)]
ist imho aber ganz gut zu lesen.

Das es Gegenteil zu filter nicht gibt ist imho eher positiv, ein not ist nicht gerade schreibarbeit, und python hat ja die "one best way"-Devise.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

keppla, danke für die Antwort, aber du hast gelesen, dass ich extra noch schrieb, dass ich nicht nach der funktionalen Variante suche. List comprehension mit "if not" schlägt sicherlich das lambda, ist aber ansonsten genauso funktional.

Stefan
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

sma hat geschrieben:Die zweite Variante funktioniert nur wenn alle Objekte bzgl. __eq__ (oder was auch immer `remove()` für den Vergleich heranzieht) unterschiedlich sind. Außerdem kann man IMHO das `[:]` leicht übersehen.
Diese Variante sollte trotzdem immer funktionieren, so lange soll_weg und __eq__(obwohl ich eher vermute, dass die Objektidentität herangezogen wird, an dieser Stelle schweigt sich die Doku aus) keine Seiteneffekte haben.

Ansonsten würde wohl eine kleine Abwandlung deiner 3. Variante helfen.

Code: Alles auswählen

kann_bleiben = lambda obj: not soll_weg(obj)
sequence[:] = filter(kann_bleiben, sequence) # nicht das [:] übersehen ;)
BlackJack

@sma: Ich würde Deine erste Variante nehmen. Auf keinen Fall Deine zweite denn damit hast Du Dir eine noch schlechtere Laufzeit eingehandelt, weil das `remove()` ja wieder sequenziell von vorne durch die Liste geht.

Weitere Alternative: Zwei Durchgänge. Einen zum Sammeln der Indizes und einen weiteren der die Elemente löscht.

Oder du fügst keppla's Lösung noch ``[:]`` hinzu:

Code: Alles auswählen

sequence[:] = [item for item in sequence if not soll_weg(item)]
Geht natürlich auch mit einem Generatorausdruck an Stelle der "list comprehension".

Dass das alles so unhandlich ist, ist IMHO ein gutes Zeichen, dass man das nicht besonders oft machen wollen sollte. :-)

Es gibt übrigens `itertools.ifilterfalse()`.

Edit:
@Darii: `list.remove()` verwendet `__eq__` oder `__cmp__`.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

@ Blackjack: wieso wusste ich, dass hier noch ein Vorschlag/Kommentar von dir zu den itertools kommt? ^^ :)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Vielen Dank für den Vorschlag mit `sequence[:] = `. Ich wusste doch, das ich etwas übersehen hatte :)

Stefan
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

meneliel hat geschrieben:@ Blackjack: wieso wusste ich, dass hier noch ein Vorschlag/Kommentar von dir zu den itertools kommt? ^^ :)
Weil man `itertools` fast an jeder Ecke gebrauchen kann.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Y0Gi hat geschrieben:
meneliel hat geschrieben:@ Blackjack: wieso wusste ich, dass hier noch ein Vorschlag/Kommentar von dir zu den itertools kommt? ^^ :)
Weil man `itertools` fast an jeder Ecke gebrauchen kann.
Functional programming ftw.

itertools, functools und operator sind da eben die Standardmodule.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten