Wie man Listenelemente zu löschen, während die Liste selbst ohne doppelte es Radfahren

StackOverflow https://stackoverflow.com/questions/2233388

  •  19-09-2019
  •  | 
  •  

Frage

verlor ich ein wenig Zeit in diesem Python for-Anweisung:

class MyListContainer:
    def __init__(self):
        self.list = []

    def purge(self):
        for object in self.list:
            if (object.my_cond()):
                self.list.remove(object)
        return self.list

container = MyListContainer()

# now suppose both obj.my_cond() return True
obj1 = MyCustomObject(par)
obj2 = MyCustomObject(other_par)

container.list = [obj1, obj2]

# returning not an empty list but [obj2]
container.purge()

Es funktioniert nicht, wie ich erwartet hatte, weil, wenn der Zyklus in „Säuberung“ das erste Objekt in der Liste löschen die zweite an den Anfang der Liste verschoben wird und der Zyklus beendet ist.

Ich löste das Duplizieren self.list vor dem für den Zyklus:

...
local_list = self.list[:]
for object in local_list:
...

Ich nehme an, dass die für die Erklärung aufhören zu arbeiten, weil ich die Länge der ursprünglichen Liste bin zu ändern. Kann jemand diesen Punkt klären?

Und ist es eine „elegante“ Art und Weise, dieses Problem zu lösen? Wenn ich mehr als einige Elemente innerhalb der Liste habe, dupliziert es nicht jedes Mal, nicht eine gute Idee zu sein scheint.

Vielleicht ist der Filter () Funktion ist die richtige, aber ich wünsche einen anderen Ansatz, wenn eine haben.

Ich bin ein Neuling.


Um Ihre nützlichen Antworten zusammenfassen:

  • Nie bearbeiten eine Liste, die Sie Looping
  • Duplizieren Sie die Liste oder die Verwendung Listenkomprehensionen
  • Verschwenden Sie konnte nicht Ihr Gedächtnis
  • eine Liste Duplizierung oder in diesem Fall, der Geist ist über es
War es hilfreich?

Lösung

Filter (oder Liste Verständnis) ist der Weg zu gehen. Wenn Sie es inplace tun wollen, so etwas wie dies funktionieren würde:

purge = []
for i,object in enumerate(self.list):
    if object.mycond()
        purge.append(i)
for i in reversed(purge):
    del self.list[i]

oder alternativ kann die Löschliste mit einem Verständnis gemacht werden, eine Verknüpfung Version aussieht wie:

for i in reversed([ i for (i,o) in enumerate(self.list) if o.mycond() ]):
    del self.list[i]

Andere Tipps

Versuchen Sie nicht. Nur nicht. Machen Sie eine Kopie oder erzeugen Sie eine neue Liste.

So stellen Sie sich eine neue Liste:

def purge(self):
    self.list = [object for object in self.list if not object.my_cond()]
    return self.list

Reserve jede Optimierung, bis Sie profiliert haben und festgestellt, dass diese Methode wirklich der Engpass Ihrer Anwendung ist. (Ich wette, es wird nicht sein.)

In Python-Variablen sind tatsächlich Daten-Etiketten. eine Liste Duplizierung ist, zum größten Teil, einen neuen Satz von Zeigern auf die Daten aus der ersten Liste zu machen. Fühlen Sie sich nicht allzu schlecht über sie.

Liste Comprehensions sind dein Freund.

z.

>>> a = range(20)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> [ x for x in a if x % 2 == 0 ]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Ihre zweite Lösung, in der Sie die Liste dupliziert ist der richtige Weg zu gehen. Danach können Sie einfach die alte Liste mit dem Duplikat ersetzen, wenn es sein muss.

Es ist absolut sicher, die Liste an seinem Platz zu verkürzen, wenn Sie es in umgekehrter Richtung zu tun!

>>> a=range(20)
>>> for i in reversed(range(len(a))):
...     if a[i]%2: del a[i]
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Eine andere Möglichkeit ist es, die ganze Scheibe neu zuweisen

>>> a=range(20)
>>> a[:]=(x for x in a if not x%2)
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Wenn die Elemente in der Liste eindeutig sind, funktioniert dies auch

>>> a=range(20)
>>> for item in reversed(a):
...  if item%2: a.remove(item)
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Hier ist etwas mehr Erklärung als Antwort auf Yuris Kommentar

Angenommen, wir haben

>>> a=[0,1,2,3,4,5]

versucht nun naiv die 3. und 4. Elemente zu löschen

>>> del a[3]
>>> del a[4]
>>> a
[0, 1, 2, 4] # didn't work because the position of all the item with index >=3 was changed

Allerdings, wenn wir tun, die del ist in umgekehrter Reihenfolge

>>> a=[0,1,2,3,4,5]
>>> del a[4]
>>> del a[3]
>>> a
[0, 1, 2, 5] # this is the desired result

erweitert nun die Idee über eine for-Schleife mit einer Entfernung Zustand, und Sie sehen, dass die Entfernung von der Live-Liste ist möglich

indeces = []
minus = 0

for i in range(self.list):
    if cond(self.list[i]):
        indeces.append(i)

for i in indeces:
    self.list = self.list[:(i-minus)].extend(self.list[i-minus+1:])
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top