Понимание списка с дублированным вызовом функции [duplicate]

этот вопрос уже есть ответ здесь:

Я хочу преобразовать строку, такую как:

'   1   ,   2  ,    ,   ,   3   '

в список непустых элементов:

['1', '2', '3']

мое решение-это понимание списка:

print [el.strip() for el in mystring.split(",") if el.strip()]

просто интересно, есть ли хороший, питонический способ написать это понимание без вызова el.strip() в два раза?

7 ответов


вы можете использовать генератор внутри списка осмысления:

  [x for x in (el.strip() for el in mylist.split(",")) if x]
#             \__________________ ___________________/
#                                v
#                        internal generator

генератор таким образом обеспечит разделенные элементы, и мы перебираем генератор, и только проверяем truthiness. Мы таким образом экономим на el.strip() звонки.

вы также можете использовать map(..) для этого (что делает его более функциональным):

  [x for x in map(str.strip, mylist.split(",")) if x]
#             \______________ ________________/
#                            v
#                           map

но это в основном то же самое (хотя логика генератора - на мой взгляд-лучше инкапсулированный.)


в качестве простой альтернативы, чтобы получить список непустых элементов (в дополнение к предыдущим хорошим ответам):

import re

s = '   1   ,   2  ,    ,   ,   3   '
print(re.findall(r'[^\s,]+', s))

вывод:

['1', '2', '3']

как насчет regex чтобы извлечь все числа из строки

import re

a = '   1   ,   2  ,    ,   ,   3   '
print(re.findall(r'\d+', a))

выход:

['1', '2', '3']

всего в одной строке кода, которая примерно так же лаконична, как вы собираетесь получить. Конечно, если вы хотите получить причудливый вы можете попробовать функциональный подход:

filter(lambda x: x, map(lambda x: x.strip(), mylist.split(',')))

но это дает вам краткость в обмен на видимость


перейти полный функционал с map и filter использование:

s = '   1   ,   2  ,    ,   ,   3   '
res = filter(None, map(str.strip, s.split(',')))

хотя это похоже на ответ @omu_negru, это позволяет избежать использования lambdas, которые, возможно, довольно уродливы, но также замедляют работу.

аргумент None фильтр переводится как: фильтр на truthness, по существу x for x in iterable if x, а map просто отображает метод str.strip (который имеет значение разделения по умолчанию пробелов) для iterable, полученного из s.split(',').

На Python 2, где filter по-прежнему возвращает список, этот подход должен легко вытеснить другие подходы в скорости.


в Python 3 нужно было бы использовать:

res = [*filter(None, map(str.strip, s.split(',')))]

для того, чтобы получить список обратно.


Если вы импортировали "re", то re.сплит() будет работать:

import re
s='   1   ,   2  ,    ,   ,   3   '
print ([el for el in re.split(r"[, ]+",s) if el])
['1', '2', '3']

Если строки, разделенные только пробелами (без промежуточной запятой), не должны быть разделены, то это будет работать:

import re
s=' ,,,,,     ,,,,  1   ,   2  ,    ,   ,   3,,,,,4   5, 6   '
print ([el for el in re.split(r"\s*,\s*",s.strip()) if el])
['1', '2', '3', '4   5', '6']

список понимания замечательны, но это не незаконно использовать более одной строки кода! Вы даже можете - боже упаси-использовать петлю for!

result = []
for el in mystring.split(",")
    x = el.strip()
    if x:
        result.append(x)

вот две-line версия. На самом деле это то же самое, что и принятый ответ Виллема ван Онсема, но с именем, данным подвыражению (и генератором, измененным на список, но это не имеет значения для такой маленькой проблемы). На мой взгляд, это делает его гораздо легче читать, несмотря на чуть более код.

all_terms = [el.strip() for el in mystring.split(",")]
non_empty_terms = [x for x in all_terms if x]

некоторые из других ответов, безусловно, короче, но я не уверен, что любой из них проще/легче понять. На самом деле, я думаю, что лучший ответ-это только один в вашем вопросе, потому что повторение в этом случае довольно незначительно.