358 Stimmen

Wie löscht man einen Eintrag in einer Liste, wenn er existiert?

Ich bekomme new_tag aus einem Textfeld eines Formulars mit self.response.get("new_tag") y selected_tags von Kontrollkästchenfeldern mit

self.response.get_all("selected_tags")

Ich kombiniere sie folgendermaßen:

tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

( f1.striplist ist eine Funktion, die Leerzeichen innerhalb der Zeichenketten in der Liste entfernt).

Aber für den Fall, dass tag_list ist leer (es werden keine neuen Tags eingegeben), aber es gibt einige selected_tags , new_tag_list eine leere Zeichenkette enthält " " .

Zum Beispiel, von logging.info :

new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']

Wie werde ich die leere Zeichenfolge los?

Wenn die Liste eine leere Zeichenkette enthält:

>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']

Aber wenn es keine leere Zeichenkette gibt:

>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
        i = s.index("")
        del s[i]
    else:
        print "new_tag_list has no empty string"

Aber das gibt:

Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    if new_tag_list.index(""):
        ValueError: list.index(x): x not in list

Warum passiert das, und wie kann ich es umgehen?

932voto

Paulo Scardine Punkte 67246

1) Fast englischer Stil:

Testen Sie auf Anwesenheit mit dem in Operator, dann die Anwendung der remove método.

if thing in some_list: some_list.remove(thing)

Les remove Methode wird nur das erste Vorkommen von thing Um alle Vorkommen zu entfernen, können Sie while anstelle von if .

while thing in some_list: some_list.remove(thing)    
  • Einfach genug, wahrscheinlich meine Wahl. für kleine Listen (kann Einzeilern nicht widerstehen)

2) Ente getippt , EAFP Stil:

Diese Einstellung, zuerst zu schießen und dann Fragen zu stellen, ist in Python weit verbreitet. Anstatt im Voraus zu testen, ob das Objekt geeignet ist, führen Sie einfach die Operation aus und fangen relevante Ausnahmen ab:

try:
    some_list.remove(thing)
except ValueError:
    pass # or scream: thing not in some_list!
except AttributeError:
    call_security("some_list not quacking like a list!")

Natürlich ist die zweite Ausnahmeklausel im obigen Beispiel nicht nur von zweifelhaftem Humor, sondern auch völlig unnötig (es ging darum, die Enten-Typisierung für Leute zu veranschaulichen, die mit dem Konzept nicht vertraut sind).

Wenn Sie erwarten, dass eine Sache mehrmals vorkommt:

while True:
    try:
        some_list.remove(thing)
    except ValueError:
        break
  • für diesen speziellen Anwendungsfall ein wenig langatmig, aber in Python sehr idiomatisch.
  • Dies ist besser als die Nummer 1
  • PEP 463 schlug eine kürzere Syntax für die einfache Verwendung von try/except vor, die hier nützlich wäre, aber sie wurde nicht angenommen.

Doch mit contextlibs suppress() Kontextmanager (eingeführt in Python 3.4) kann der obige Code wie folgt vereinfacht werden:

with suppress(ValueError, AttributeError):
    some_list.remove(thing)

Nochmals, wenn Sie erwarten, dass eine Sache mehrfach vorkommt:

with suppress(ValueError):
    while True:
        some_list.remove(thing)

3) Funktionaler Stil:

Um 1993 erhielt Python lambda , reduce() , filter() y map() mit freundlicher Genehmigung eines Lispeln Hacker, der sie übersehen und funktionierende Patches* eingereicht hat. Sie können verwenden filter um Elemente aus der Liste zu entfernen:

is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)

Es gibt eine Abkürzung, die für Ihren Fall nützlich sein könnte: Wenn Sie leere Einträge herausfiltern möchten (d. h. Einträge, bei denen bool(item) == False , wie None , Null, leere Zeichenketten oder andere leere Sammlungen), können Sie None als erstes Argument übergeben:

cleaned_list = filter(None, some_list)
  • [Update] : in Python 2.x, filter(function, iterable) war früher gleichbedeutend mit [item for item in iterable if function(item)] (oder [item for item in iterable if item] wenn das erste Argument None ); in Python 3.x ist es jetzt äquivalent zu (item for item in iterable if function(item)) . Der feine Unterschied ist, dass filter früher eine Liste zurückgab, jetzt funktioniert es wie ein Generatorausdruck - das ist OK, wenn Sie nur über die bereinigte Liste iterieren und sie verwerfen, aber wenn Sie wirklich eine Liste brauchen, müssen Sie die filter() Aufruf mit dem list() Konstrukteur.
  • *Diese Lispy-artigen Konstrukte gelten in Python als etwas fremdartig. Um 2005, Guido sprach sogar davon, die filter - zusammen mit Begleitern map y reduce (sie sind noch nicht weg, aber reduce wurde in die functools Modul, das einen Blick wert ist, wenn Sie mögen Funktionen hoher Ordnung ).

4) Mathematischer Stil:

Listenauffassungen wurde zum bevorzugten Stil für die Listenmanipulation in Python seit der Einführung in Version 2.0 durch PEP 202 . Der Grundgedanke dahinter ist, dass List comprehensions eine prägnantere Möglichkeit bieten, Listen in Situationen zu erstellen, in denen map() y filter() und/oder verschachtelte Schleifen verwendet werden.

cleaned_list = [ x for x in some_list if x is not thing ]

Generatorausdrücke wurden in Version 2.4 eingeführt durch PEP 289 . Ein Generatorausdruck eignet sich besser für Situationen, in denen Sie nicht unbedingt eine vollständige Liste im Speicher erstellen müssen (oder wollen) - z. B. wenn Sie nur ein Element nach dem anderen durchlaufen wollen. Wenn Sie nur über die Liste iterieren, können Sie sich einen Generatorausdruck als eine faul ausgewertet das Verständnis der Liste:

for item in (x for x in some_list if x is not thing):
    do_your_thing_with(item)

Anmerkungen

  1. können Sie den Ungleichheitsoperator verwenden != anstelle von is not ( der Unterschied ist wichtig )
  2. für Kritiker von Methoden, die eine Listenkopie voraussetzen: Entgegen der landläufigen Meinung sind Generatorausdrücke nicht immer effizienter als Listenauflösungen - bitte informieren Sie sich, bevor Sie sich beschweren

13voto

Tim Pietzcker Punkte 311448
try:
    s.remove("")
except ValueError:
    print "new_tag_list has no empty string"

Beachten Sie, dass dadurch nur eine Instanz der leeren Zeichenkette aus Ihrer Liste entfernt wird (so wie es auch Ihr Code getan hätte). Kann Ihre Liste mehr als einen Eintrag enthalten?

13voto

qwerty_url Punkte 333

In einem Satz:

>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> s.remove('') if '' in s else None # Does nothing if '' not in s
>>> s
['Hello', 'Cool', 'Glam']
>>>

9voto

phihag Punkte 261131

Si index die gesuchte Zeichenkette nicht findet, löst es den ValueError die Sie gerade sehen. Entweder fangen Sie den ValueError ab:

try:
    i = s.index("")
    del s[i]
except ValueError:
    print "new_tag_list has no empty string"

ou 使い道 find die in diesem Fall -1 zurückgibt.

i = s.find("")
if i >= 0:
    del s[i]
else:
    print "new_tag_list has no empty string"

7voto

ideasman42 Punkte 35167

Der Vollständigkeit halber füge ich diese Antwort hinzu, obwohl sie nur unter bestimmten Bedingungen verwendbar ist.

Wenn Sie sehr große Listen haben, vermeidet das Entfernen vom Ende der Liste, dass die CPython-Interna die memmove für Situationen, in denen Sie die Liste neu ordnen können. Es bringt einen Leistungsgewinn, vom Ende der Liste zu entfernen, da es nicht notwendig ist memmove jede Element nach dem zu entfernenden Element - einen Schritt zurück (1) .
Bei einmaligen Entfernungen kann der Leistungsunterschied akzeptabel sein, aber wenn Sie eine große Liste haben und viele Elemente entfernen müssen, werden Sie wahrscheinlich einen Leistungsabfall feststellen.

Zugegebenermaßen ist in diesen Fällen eine vollständige Listensuche wahrscheinlich auch ein Leistungsengpass, es sei denn, die Elemente befinden sich überwiegend am Anfang der Liste.

Diese Methode kann für eine effizientere Entfernung verwendet werden,
solange die Neuordnung der Liste akzeptabel ist. (2)

def remove_unordered(ls, item):
    i = ls.index(item)
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()

Sie möchten vielleicht vermeiden, dass ein Fehler auftritt, wenn die item ist nicht in der Liste enthalten.

def remove_unordered_test(ls, item):
    try:
        i = ls.index(item)
    except ValueError:
        return False
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()
    return True

  1. Während ich dies mit CPython getestet habe, ist es sehr wahrscheinlich, dass die meisten/alle anderen Python-Implementierungen ein Array verwenden, um Listen intern zu speichern. Wenn sie also keine ausgefeilte Datenstruktur verwenden, die für die effiziente Größenänderung von Listen ausgelegt ist, haben sie wahrscheinlich die gleiche Leistungscharakteristik.

Eine einfache Möglichkeit, dies zu testen, besteht darin, den Geschwindigkeitsunterschied zwischen dem Entfernen vom Anfang der Liste und dem Entfernen des letzten Elements zu vergleichen:

python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'

Mit:

python -m timeit 'a = [0] * 100000' 'while a: a.pop()'

(ergibt einen Geschwindigkeitsunterschied in der Größenordnung, wobei das zweite Beispiel mit CPython und PyPy schneller ist).

  1. In diesem Fall könnten Sie die Verwendung einer set vor allem dann, wenn die Liste nicht für die Speicherung von Duplikaten gedacht ist.
    In der Praxis kann es jedoch vorkommen, dass Sie veränderliche Daten speichern müssen, die nicht in eine set . Prüfen Sie auch auf btree's, ob die Daten geordnet werden können.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X