Seite 1 von 1

Liste aufzählen und dabei Elemente entfernen?

Verfasst: Mittwoch 1. Oktober 2008, 15:59
von sma
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

Verfasst: Mittwoch 1. Oktober 2008, 16:02
von keppla
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.

Verfasst: Mittwoch 1. Oktober 2008, 16:15
von sma
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

Re: Liste aufzählen und dabei Elemente entfernen?

Verfasst: Mittwoch 1. Oktober 2008, 16:53
von Darii
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 ;)

Verfasst: Mittwoch 1. Oktober 2008, 16:58
von 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__`.

Verfasst: Mittwoch 1. Oktober 2008, 18:36
von meneliel
@ Blackjack: wieso wusste ich, dass hier noch ein Vorschlag/Kommentar von dir zu den itertools kommt? ^^ :)

Verfasst: Donnerstag 2. Oktober 2008, 08:57
von sma
Vielen Dank für den Vorschlag mit `sequence[:] = `. Ich wusste doch, das ich etwas übersehen hatte :)

Stefan

Verfasst: Donnerstag 2. Oktober 2008, 10:33
von Y0Gi
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.

Verfasst: Donnerstag 2. Oktober 2008, 15:32
von Leonidas
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.