9

Удалить пустые строки из списка строк

11

Я хочу удалить все пустые строки из списка строк в Python.

Мой подход выглядит так:

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

Есть ли более питонический способ сделать это?

5 ответ(ов)

1

Функция filter действительно имеет специальный параметр для этой цели:

filter(None, sequence)

Этот вызов отфильтрует все элементы, которые оцениваются как False. Вам не нужно использовать никакие вызовы, такие как bool, len и так далее.

По производительности это аналогично map(bool, ...).

0

В данном примере мы сравниваем два способа удаления пустых строк из списка строк с использованием Python: метод join() и filter().

Исходный список строк:

lstr = ['hello', '', ' ', 'world', ' ']

При использовании ' '.join(lstr).split() мы получаем результат:

['hello', 'world']

Этот подход объединяет все элементы списка в одну строку, разделяя их пробелами, а затем разбивает строку обратно на список, удаляя все временные пробелы и пустые строки.

Сравнительно с этим, когда мы применяем filter(None, lstr), мы получаем:

['hello', ' ', 'world', ' ']

Этот метод удаляет только пустые строки (''), оставляя строки с пробелами (' ').

Теперь рассмотрим производительность. Мы можем провести тестирование времени выполнения для обоих методов, используя библиотеку timeit.

from timeit import timeit

# Время выполнения join и split
time_join_split = timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
print(time_join_split)  # Примерный результат: 4.226747989654541

# Время выполнения filter
time_filter = timeit('filter(None, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
print(time_filter)  # Примерный результат: 3.0278358459472656

Хотя filter(None, lstr) быстрее, он не удаляет пробелы как строки. Чтобы использовать filter() для удаления строк с пробелами, нам нужно создать новый список с заменой пробелов:

# Время выполнения filter с удалением пробелов
time_filter_with_space_removal = timeit('filter(None, [l.replace(" ", "") for l in lstr])', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
print(time_filter_with_space_removal)  # Примерный результат: 18.101892948150635

Таким образом, выводя результаты, можно заметить, что метод ' '.join(lstr).split() более эффективен по времени, но filter() в этом случае непрактичен, если требуется обработка строк с пробелами.

0

Обзор лучших решений:

1. Удаление пустых строк БЕЗ удаления пробелов:

То есть, строки, состоящие только из пробелов, сохраняются:

slist = list(filter(None, slist))

Плюсы:

  • Непростое решение;
  • Быстрое (см. среднее время на примерах ниже).

2. Удаление пустых строк после удаления лишних пробелов ...

2.a ... когда строки НЕ содержат пробелов между словами:

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

Плюсы:

  • Небольшой объем кода;
  • Быстрое выполнение. (НО не самое быстрое для больших наборов данных из-за использования памяти, в противовес результатам @paolo-melchiorre)

2.b ... когда строки содержат пробелы между словами?

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

Плюсы:

  • Самое быстрое решение;
  • Легкость понимания кода.

Тесты производительности на машине 2018 года:

## Создание тестовых данных
#
import random, string
nwords = 10000
maxlen = 30
null_ratio = 0.1
rnd = random.Random(0)                  # детерминированные результаты
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)
        ]

## Тестирование функций
#
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):  # затраты памяти
    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):  # слова без(!) пробелов
    return ' '.join(slist).split()

## Тесты производительности
#
%timeit nostrip_filter(words)
142 µs ± 16.8 µs за цикл (среднее ± стандартное отклонение из 7 запусков, 10000 циклов каждый)

%timeit nostrip_comprehension(words)
263 µs ± 19.1 µs за цикл (среднее ± стандартное отклонение из 7 запусков, 1000 циклов каждый)

%timeit strip_filter(words)
653 µs ± 37.5 µs за цикл (среднее ± стандартное отклонение из 7 запусков, 1000 циклов каждый)

%timeit strip_filter_map(words)
642 µs ± 36 µs за цикл (среднее ± стандартное отклонение из 7 запусков, 1000 циклов каждый)

%timeit strip_filter_comprehension(words)
693 µs ± 42.2 µs за цикл (среднее ± стандартное отклонение из 7 запусков, 1000 циклов каждый)

%timeit strip_filter_generator(words)
750 µs ± 28.6 µs за цикл (среднее ± стандартное отклонение из 7 запусков, 1000 циклов каждый)

%timeit strip_join_split(words)
796 µs ± 103 µs за цикл (среднее ± стандартное отклонение из 7 запусков, 1000 циклов каждый)
0

Ответ от @Ib33X действительно замечательный. Если вы хотите удалить все пустые строки после удаления пробелов, вам также нужно использовать метод strip(). В противном случае вы получите пустую строку, если она содержит только пробелы. Например, строка " " будет считаться допустимой. Таким образом, можно достичь желаемого результата с помощью следующего кода:

strings = ["first", "", "second ", " "]
[x.strip() for x in strings if x.strip()]

Результатом будет ["first", "second"].

Если вы хотите использовать метод filter, это можно сделать так:

list(filter(lambda item: item.strip(), strings))

Это также вернёт тот же самый результат.

0

Вместо проверки на if x, я бы использовал if x != '', чтобы просто исключить пустые строки. Вот так:

str_list = [x for x in str_list if x != '']

Это позволит сохранить элементы типа None в вашем списке. Также, если в списке есть целые числа и 0 является одним из них, он тоже будет сохранён.

Например:

str_list = [None, '', 0, "Hi", '', "Hello"]
[x for x in str_list if x != '']

Этот код вернёт:

[None, 0, "Hi", "Hello"]

Таким образом, вы получите список, из которого удалены только пустые строки, а остальные элементы останутся нетронутыми.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь