978 Stimmen

Entfernen Sie leere Zeichenfolgen aus einer Liste von Zeichenfolgen

Ich möchte alle leeren Strings aus einer Liste von Strings in Python entfernen.

Meine Idee sieht so aus:

while '' in str_list:
    str_list.remove('')

Gibt es einen pythonischeren Weg, um dies zu tun?

50 Stimmen

@Ivo, keiner dieser Aussagen ist wahr. Sie sollten niemals eine Liste ändern, über die Sie iterieren, indem Sie for x in list verwenden. Wenn Sie eine while-Schleife verwenden, ist es in Ordnung. Die demonstrierte Schleife entfernt leere Zeichenfolgen, bis keine leeren Zeichenfolgen mehr vorhanden sind, und stoppt dann. Ich hatte mir tatsächlich nicht einmal die Frage angesehen (nur den Titel), aber ich habe mit genau derselben Schleife als Möglichkeit geantwortet! Wenn Sie keine Verständniskomprehensionen oder Filter verwenden möchten, um Speicherplatz zu sparen, ist dies eine sehr pythonische Lösung.

4 Stimmen

Noch ein sehr gültiger Punkt, die Liste, über die Sie iterieren, niemals zu ändern :)

1 Stimmen

@EduardLuca, wenn der Zweck des Iterierens über eine Liste darin besteht, sie zu ändern, dann ist das genau das Gegenteil von dem, was du tun solltest. Du musst nur darauf achten, dass du dabei kein unerwartetes Verhalten verursachst.

1549voto

livibetter Punkte 18322

Ich würde filter verwenden:

str_list = filter(None, str_list)
str_list = filter(bool, str_list)
str_list = filter(len, str_list)
str_list = filter(lambda item: item, str_list)

In Python 3 gibt filter einen Iterator zurück, daher sollte er in einem Aufruf von list() eingehüllt werden

str_list = list(filter(None, str_list))

0 Stimmen

Vielleicht, aber timeit zeigt den Unterschied, und die Verwendung von None ist leserlicher und klarer - meiner Meinung nach! Oder ich würde sagen, die Verwendung von filter(None, l1) wird auf eine pythonische Weise aussehen.

19 Stimmen

Wenn Sie so auf Leistung gedrängt sind, ist itertools's ifilter sogar schneller—>>> timeit('filter(None, str_list)', 'str_list=["a"]*1000', number=100000) 2.3468542098999023; >>> timeit('itertools.ifilter(None, str_list)', 'str_list=["a"]*1000', number=100000) 0.04442191123962402.

1 Stimmen

@BeauMartínez die Zeit für itertools.ifilter ist nicht vollständig genau, da der Generator noch nicht ausgewertet wurde. Es sollte mit list() umschlossen werden. filter(bool) -> 1.367577075958252, ifilter(bool) -> 0.032318115234375, list(ifilter(bool)) -> 1.8174781799316406

438voto

Ib33X Punkte 5954

Die Verwendung eines Listen-Verständnisses ist der Pythonischste Weg:

>>> strings = ["first", "", "second"]
>>> [x for x in strings if x]
['first', 'second']

Wenn die Liste direkt geändert werden muss, weil es andere Referenzen gibt, die die aktualisierten Daten sehen müssen, dann verwenden Sie eine Slice-Zuweisung:

strings[:] = [x for x in strings if x]

42 Stimmen

Ich mag diese Lösung, weil sie leicht anpassbar ist. Wenn ich nicht nur leere Zeichenfolgen, sondern auch Zeichenfolgen entfernen müsste, die nur Leerzeichen sind, zum Beispiel: [x für x in Zeichenfolgen, wenn x.strip()].

1 Stimmen

[x für x in strings if x] Das funktioniert gut, aber können Sie bitte erklären, wie diese Schleife funktioniert??

8 Stimmen

@AmarKumar In Python wird ein leerer String in einem booleschen Kontext wie in if x zu false ausgewertet. Die Klammern, die for-Schleife und die if-Klausel kombinieren, um "eine Liste zu generieren, die aus x für jedes Element in strings besteht, wenn x tatsächlich etwas enthält." @Ib33x Absolut fantastische Arbeit. Diese Antwort ist sicherlich die Pythonischste.

110voto

Ivo van der Wijk Punkte 15553

Der Filter hat tatsächlich eine spezielle Option dafür:

filter(None, sequence)

Er wird alle Elemente filtern, die zu False ausgewertet werden. Es ist nicht nötig, hier einen tatsächlichen Aufruf wie bool, len und so weiter zu verwenden.

Es ist genauso schnell wie map(bool, ...)

9 Stimmen

Dies ist tatsächlich ein Python-Idiom. Es ist auch die einzige Zeit, in der ich filter() noch verwende, Listenverständnisse haben überall sonst übernommen.

0 Stimmen

Ich finde es einfacher, die Absicht des Codes zu erkennen, im Vergleich zu einer Listenkomprehension.

35voto

Aziz Alto Punkte 16611
>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']

>>> ' '.join(lstr).split()
['hello', 'world']

>>> filter(None, lstr)
['hello', ' ', 'world', ' ']

Vergleiche Zeit

>>> from timeit import timeit
>>> timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
4.226747989654541
>>> timeit('filter(None, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
3.0278358459472656

Beachten Sie, dass filter(None, lstr) keine leeren Zeichenfolgen mit einem Leerzeichen ' ' entfernt, es entfernt nur '', während ' '.join(lstr).split() beide entfernt.

Um filter() mit Leerzeichenzeichenfolgen entfernt zu verwenden, dauert es viel länger:

>>> timeit('filter(None, [l.replace(" ", "") for l in lstr])', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
18.101892948150635

0 Stimmen

Es wird nicht funktionieren, wenn Sie Leerzeichen innerhalb des Strings eines Wortes haben. zum Beispiel: ['hello world', ' ', 'hello', ' '] . >> ['helloworld', ' ', 'hello', ' '] haben Sie eine andere Lösung, um Leerzeichen innerhalb eines Elements in der Liste zu behalten, aber andere zu entfernen?

1 Stimmen

Beachten Sie, dass filter(None, lstr) keine leeren Zeichenfolgen mit einem Leerzeichen ' ' entfernt Das liegt daran, dass es sich nicht um eine leere Zeichenfolge handelt.

0 Stimmen

Lebensretter !!

32voto

ankostis Punkte 7409

Fassen Sie die besten Antworten zusammen:

1. Entfernen von Leerelementen OHNE Entfernung:

D. h. alle Leerzeichen bleiben erhalten:

slist = list(filter(None, slist))

PROs:

  • einfachste;
  • schnellste (siehe Benchmarks unten).

2. Leerzeichen nach dem Entfernen eliminieren ...

2.a ... wenn Zeichenfolgen KEINE Leerzeichen zwischen den Wörtern enthalten:

slist = ' '.join(slist).split()

PROs:

  • kleiner Code
  • schnell (ABER nicht der schnellste bei großen Datensätzen aufgrund des Speichers, im Gegensatz zu den Ergebnissen von @paolo-melchiorre)

2.b ... wenn Zeichenfolgen Leerzeichen zwischen den Wörtern enthalten?

slist = list(filter(str.strip, slist))

PROs:

  • der schnellste;
  • Verständlichkeit des Codes.

Benchmarks auf einem Computer aus dem Jahr 2018:

## Testdaten erstellen
#
import random, string
nwords = 10000
maxlen = 30
null_ratio = 0.1
rnd = random.Random(0)                  # deterministische Ergebnisse
words = [' ' * rnd.randint(0, maxlen)
         if rnd.random() > (1 - null_ratio)
         else
         ''.join(random.choices(string.ascii_letters, k=rnd.randint(0, maxlen)))
         for _i in range(nwords)
        ]

## Testfunktionen
#
def nostrip_filter(slist):
    return list(filter(None, slist))

def nostrip_comprehension(slist):
    return [s for s in slist if s]

def strip_filter(slist):
    return list(filter(str.strip, slist))

def strip_filter_map(slist): 
    return list(filter(None, map(str.strip, slist))) 

def strip_filter_comprehension(slist):  # verschwendet Speicher
    return list(filter(None, [s.strip() for s in slist]))

def strip_filter_generator(slist):
    return list(filter(None, (s.strip() for s in slist)))

def strip_join_split(slist):  # Wörter ohne(!) Leerzeichen
    return ' '.join(slist).split()

## Benchmarks
#
%timeit nostrip_filter(words)
142 µs ± 16.8 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, 10000 Schleifen je Durchlauf)

%timeit nostrip_comprehension(words)
263 µs ± 19.1 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, 1000 Schleifen je Durchlauf)

%timeit strip_filter(words)
653 µs ± 37.5 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, 1000 Schleifen je Durchlauf)

%timeit strip_filter_map(words)
642 µs ± 36 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, 1000 Schleifen je Durchlauf)

%timeit strip_filter_comprehension(words)
693 µs ± 42.2 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, 1000 Schleifen je Durchlauf)

%timeit strip_filter_generator(words)
750 µs ± 28.6 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, 1000 Schleifen je Durchlauf)

%timeit strip_join_split(words)
796 µs ± 103 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, 1000 Schleifen je Durchlauf)

0 Stimmen

s und s.strip() können einfach zu s.strip() vereinfacht werden.

0 Stimmen

s und s.strip() sind notwendig, wenn wir filter(None, words), die akzeptierte Antwort, vollständig replizieren wollen. Ich habe die beiden Beispielfunktionen oben korrigiert und die beiden schlechten weggelassen.

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