Frage

Diese Frage hat bereits eine Antwort hier:

Wenn ich Schreibe code in Python, brauche ich oft so entfernen Sie Elemente aus einer Liste oder in anderen Sequenz-Typ basierend auf einigen Kriterien.Ich habe nicht gefunden eine Lösung, die ist elegant und effizient, so entfernen Sie Elemente aus einer Liste, die Sie derzeit Durchlaufen, ist schlecht.Für Beispiel, Sie können nicht tun dies:

for name in names:
    if name[-5:] == 'Smith':
        names.remove(name)

Ich in der Regel am Ende etwas wie das zu tun:

toremove = []
for name in names:
    if name[-5:] == 'Smith':
        toremove.append(name)
for name in toremove:
    names.remove(name)
del toremove

Dies ist innefficient, ziemlich hässlich und möglicherweise buggy (wie funktioniert es handle multiple "John Smith" - Einträge?).Hat jemand eine elegantere Lösung, oder zumindest eine mehr effizient?

Wie über eine, die funktioniert mit Wörterbüchern?

War es hilfreich?

Lösung

Zwei einfache Möglichkeiten, zu vollbringen, die nur den filtern sind:

  1. Verwendung filter:

    names = filter(lambda name: name[-5:] != "Smith", names)

  2. Liste Verstehens:

    names = [name for name in names if name[-5:] != "Smith"]

Beachten Sie, dass in beiden Fällen halten Sie die Werte, für die das Prädikat Funktion ausgewertet wird True,, so dass Sie haben, um die Logik umzukehren (D. H.Sie sagen, "halten die Menschen, die nicht über den Nachnamen Smith" anstelle von "entfernen" die Leute, die den Nachnamen Smith").

Bearbeiten Komisch...zwei Personen individuell gebucht beiden Antworten, schlug ich vor, als ich war posting mine.

Andere Tipps

Sie können auch Durchlaufen rückwärts über die Liste:

for name in reversed(names):
    if name[-5:] == 'Smith':
        names.remove(name)

Dies hat den Vorteil, dass Sie nicht erstellen Sie eine neue Liste (wie filter oder eine Liste comprehension) und verwendet einen iterator anstelle einer Liste kopieren (wie [:]).

Beachten Sie, dass, obwohl das entfernen von Elementen während der Iteration rückwärts ist sicher, einfügen, ist etwas schwieriger.

Die offensichtliche Antwort ist diejenige, die John und ein paar andere Leute gab, nämlich:

>>> names = [name for name in names if name[-5:] != "Smith"]       # <-- slower

Aber das hat den Nachteil, dass es erstellt eine neue Liste, Objekt, anstatt die Wiederverwendung der original-Objekt.Ich habe einige profiling und Experimente, und die effizienteste Methode, die ich verwende, ist:

>>> names[:] = (name for name in names if name[-5:] != "Smith")    # <-- faster

Die Zuordnung zu "Namen[:]" bedeutet im Grunde "ersetzen Sie den Inhalt der die Liste der Namen mit den folgenden Wert".Es ist anders als nur die Zuweisung zu Namen, die es nicht schaffen, ein neues list-Objekt.Der rechten Seite der Zuweisung ein generator-Ausdruck (Hinweis: die Verwendung von Klammern anstelle von eckigen Klammern).Dadurch werden Python zu iterieren über die Liste.

Einige schnelle profiling schlägt vor, dass dies etwa 30% schneller als die Liste Verständnis nähern, und über 40% schneller als der filter-Ansatz.

Caveat:während diese Lösung ist schneller als die offensichtliche Lösung ist, ist es mehr obskure, und stützt sich auf fortgeschrittenen Python-Techniken.Wenn Sie es verwenden, empfehle ich begleite ihn mit einem Kommentar.Es ist wahrscheinlich nur Wert verwenden, in Fällen, in denen Sie sich wirklich um die performance dieses bestimmten Vorgang (das ist ziemlich schnell, egal was).(In dem Fall, wo ich diese verwendet, ich war dabei Ein* * Breite Suche, und diese verwendet, um entfernen search-Punkte aus der Suche beam.)

Verwendung eine Liste Verständnis

list = [x for x in list if x[-5:] != "smith"]

Es gibt Zeiten, wenn die Filterung (entweder mit filter oder eine Liste Verständnis) nicht funktioniert.Dies geschieht, wenn einige andere Objekt einen Verweis auf die Liste, die Sie Bearbeiten, und Sie müssen, ändern Sie die Liste in den Ort.

for name in names[:]:
    if name[-5:] == 'Smith':
        names.remove(name)

Der einzige Unterschied zu den original-code ist der Einsatz von names[:] statt names in der for-Schleife.Der code iteriert über eine (flache) Kopie der Liste und die Umzüge wie erwartet funktionieren.Da die Liste kopieren ist flach, es ist ziemlich schnell.

filter wäre für diese awesome.Einfaches Beispiel:

names = ['mike', 'dave', 'jim']
filter(lambda x: x != 'mike', names)
['dave', 'jim']

Edit: Corey Liste Verständnis ist auch Super.

names = filter(lambda x: x[-5:] != "Smith", names);

Beide Lösungen, filter und Verständnis erfordert den Bau einer neuen Liste.Ich weiß nicht genug von der Python-Interna, um sicher zu sein, aber ich denken dass eine eher traditionelle (aber weniger elegant) Ansatz könnte effizienter sein:

names = ['Jones', 'Vai', 'Smith', 'Perez']

item = 0
while item <> len(names):
    name = names [item]
    if name=='Smith':
        names.remove(name)
    else:
        item += 1

print names

Jedenfalls für kurze Listen, I stick mit einem der beiden vorgeschlagenen Lösungen früher.

Zur Beantwortung Ihrer Frage über die Arbeit mit Wörterbüchern, sollten Sie beachten, dass Python 3.0 gehören dict Verstehens:

>>> {i : chr(65+i) for i in range(4)}

In der Zwischenzeit können Sie tun, eine quasi-dict Verständnis dieser Weise:

>>> dict([(i, chr(65+i)) for i in range(4)])

Oder als eine mehr direkte Antwort:

dict([(key, name) for key, name in some_dictionary.iteritems if name[-5:] != 'Smith'])

Wenn die Liste gefiltert werden soll und in der Liste Größe ist ziemlich groß, dann algorithmen, die bereits im vorherigen Antworten, die auf der Liste.die remove(), ungeeignet sein kann, weil Ihre Komplexität ist O(n^2).In diesem Fall können Sie die folgenden nicht-so pythonic-Funktion:

def filter_inplace(func, original_list):
  """ Filters the original_list in-place.

  Removes elements from the original_list for which func() returns False.

  Algrithm's computational complexity is O(N), where N is the size
  of the original_list.
  """

  # Compact the list in-place.
  new_list_size = 0
  for item in original_list:
    if func(item):
      original_list[new_list_size] = item
      new_list_size += 1

  # Remove trailing items from the list.
  tail_size = len(original_list) - new_list_size
  while tail_size:
    original_list.pop()
    tail_size -= 1


a = [1, 2, 3, 4, 5, 6, 7]

# Remove even numbers from a in-place.
filter_inplace(lambda x: x & 1, a)

# Prints [1, 3, 5, 7]
print a

Edit:Eigentlich ist die Lösung bei https://stackoverflow.com/a/4639748/274937 zu überlegen ist mir die Lösung.Es ist mehr pythonic und arbeitet schneller.So, hier ist eine neue filter_inplace () - Implementierung:

def filter_inplace(func, original_list):
  """ Filters the original_list inplace.

  Removes elements from the original_list for which function returns False.

  Algrithm's computational complexity is O(N), where N is the size
  of the original_list.
  """
  original_list[:] = [item for item in original_list if func(item)]

Der filter und die Liste Verstehens sind ok für dein Beispiel, aber Sie haben ein paar Probleme:

  • Machen Sie eine Kopie Ihrer Liste und Rückkehr Sie die neue ein, und das wird ineffizient, wenn die ursprüngliche Liste ist wirklich groß
  • Sie können sehr umständlich sein, wenn die Kriterien für die Elemente auswählen (in deinem Fall, wenn name[-5:] == 'Smith') ist komplizierter, oder mehrere Bedingungen.

Ihre ursprüngliche Lösung ist eigentlich mehr effiziente für sehr große Listen, auch wenn wir Zustimmen, dass es hässlicher.Aber wenn Sie sich sorgen machen, dass Sie können haben mehrere 'John Smith', es kann behoben werden, indem das löschen basierend auf der position und nicht auf den Wert:

names = ['Jones', 'Vai', 'Smith', 'Perez', 'Smith']

toremove = []
for pos, name in enumerate(names):
    if name[-5:] == 'Smith':
        toremove.append(pos)
for pos in sorted(toremove, reverse=True):
    del(names[pos])

print names

Wir können nicht wählen Sie eine Lösung, die ohne Rücksicht auf die Größe der Liste, aber für große Listen würde ich es bevorzugen, Ihre 2-pass-Lösung anstelle der filter oder Listen Verstehens

Im Falle des Satzes.

toRemove = set([])  
for item in mySet:  
    if item is unwelcome:  
        toRemove.add(item)  
mySets = mySet - toRemove 

Hier ist mein filter_inplace - Implementierung, die verwendet werden kann zum filtern von Elementen aus einer Liste in-place, kam ich mit diesem auf meinem eigenen, selbstständig zu finden, bevor dieser Seite.Es ist der gleiche Algorithmus wie das, was PabloG geschrieben, nur mehr generisch, so dass Sie können es verwenden, um filter-Listen in Ort, es ist auch in der Lage, entfernen Sie aus der Liste basiert auf den comparisonFunc wenn Umgekehrt gesetzt ist True;eine Art umgekehrter filter, wenn man so will.

def filter_inplace(conditionFunc, list, reversed=False):
    index = 0
    while index < len(list):
        item = list[index]

        shouldRemove = not conditionFunc(item)
        if reversed: shouldRemove = not shouldRemove

        if shouldRemove:
            list.remove(item)
        else:
            index += 1

Nun, dies ist eindeutig ein Problem mit den Daten-Struktur, die Sie sind mit.Verwenden Sie eine Hash-Tabelle für Beispiel.Einige Implementierungen unterstützen Sie mehrere Einträge pro Taste, so kann man entweder ein pop-das neueste element aus, oder entfernen Sie alle von Ihnen.

Aber das ist, und was Sie gehen, um die Lösung zu finden ist, Eleganz, durch eine andere Datenstruktur, nicht-Algorithmus.Vielleicht kann man besser machen, wenn es sortiert ist, oder etwas, aber die iteration über eine Liste, ist Ihre einzige Methode hier.

edit: man muss erkennen, er bat um "Effizienz"...alle diese vorgeschlagenen Methoden nur die Liste Durchlaufen, die ist die gleiche wie das, was er vorgeschlagen hat.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top