Как лучше, с эстетической точки зрения и с точки зрения производительности, разделить список элементов на несколько списков на основе условного обозначения? Эквивалент:
good = [x for x in mylist if x in goodvals]
bad = [x for x in mylist if x not in goodvals]
Есть ли более элегантный способ сделать это?
Обновление: вот фактический вариант использования, чтобы лучше объяснить, что я пытаюсь сделать:
# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims = [f for f in files if f[2].lower() not in IMAGE_TYPES]
str.split()
, чтобы разбить список на упорядоченную коллекцию последовательных подсписков. Напримерsplit([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6])
, в отличие от деления элементов списка по категориям.IMAGE_TYPES = set('.jpg','.jpeg','.gif','.bmp','.png')
. n (1) вместо n (o / 2), практически без разницы в читаемости.Ответы:
Этот код отлично читается и предельно понятен!
Опять же, это хорошо!
Могут быть небольшие улучшения производительности при использовании наборов, но это тривиальное различие, и я считаю, что понимание списка намного проще для чтения, и вам не нужно беспокоиться о том, что порядок будет испорчен, дубликаты будут удалены и так далее.
На самом деле, я могу сделать еще один шаг назад и просто использовать простой цикл for:
Понимание или использование списка
set()
хорошо, пока вам не понадобится добавить какую-либо другую проверку или другую логику - скажем, вы хотите удалить все 0-байтовые jpeg, вы просто добавляете что-то вроде ..источник
[x for x in blah if ...]
- многословный,lambda
неуклюжий и ограниченный ... Это похоже на вождение самой крутой машины с 1995 года сегодня. Не так, как тогда.источник
good.append(x) if x in goodvals else bad.append(x)
более читабельноx
, вы можете сделать этоappend
только одним :for x in mylist: (good if isgood(x) else bad).append(x)
(bad.append, good.append)
(good if x in goodvals else bad).append(x)
Вот ленивый подход итератора:
Он оценивает условие один раз для каждого элемента и возвращает два генератора, сначала получая значения из последовательности, где условие истинно, а другое - ложно.
Поскольку это лениво, вы можете использовать его на любом итераторе, даже бесконечном:
Обычно подход с возвратом не ленивых списков лучше:
Редактировать: для вашего более конкретного использования разделения элементов в разные списки по некоторому ключу, вот общая функция, которая делает это:
Использование:
источник
[ x for x in my_list if ExpensiveOperation(x) ]
запуск занимает много времени, вы, конечно, не хотите делать это дважды!tee
хранятся все значения между итераторами, которые он возвращает, поэтому он не сэкономит память, если вы зациклите один генератор, а затем другой.Проблема всех предложенных решений заключается в том, что он будет сканировать и применять функцию фильтрации дважды. Я бы сделал простую маленькую функцию, например:
Таким образом, вы ничего не обрабатываете дважды, а также не повторяете код.
источник
Мой взгляд на это. Я предлагаю ленивую однопроходную
partition
функцию, которая сохраняет относительный порядок в выходных подпоследовательностях.1. Требования
Я предполагаю, что требования:
i
)filter
илиgroupby
)2.
split
библиотекаМоя
partition
функция (представленная ниже) и другие подобные функции превратили ее в небольшую библиотеку:Обычно устанавливается через PyPI:
Чтобы разделить список по условию, используйте
partition
функцию:3.
partition
объясненная функцияВнутренне нам нужно построить две подпоследовательности одновременно, поэтому использование только одной выходной последовательности приведет к вычислению и другой. И нам нужно сохранять состояние между пользовательскими запросами (хранить обработанные, но еще не запрошенные элементы). Чтобы сохранить состояние, я использую две двусторонние очереди (
deques
):SplitSeq
класс заботится о ведении домашнего хозяйства:Волшебство происходит в его
.getNext()
методе. Это почти как.next()
итераторы, но позволяет указать, какой тип элемента мы хотим на этот раз. За сценой он не отбрасывает отклоненные элементы, а помещает их в одну из двух очередей:Конечный пользователь должен использовать
partition
функцию. Он принимает функцию условия и последовательность (точно так же какmap
илиfilter
) и возвращает два генератора. Первый генератор создает подпоследовательность элементов, для которых выполняется условие, второй - дополнительную подпоследовательность. Итераторы и генераторы допускают ленивое расщепление даже длинных или бесконечных последовательностей.Я выбрал тестовую функцию в качестве первого аргумента, чтобы облегчить частичное применение в будущем (аналогично тому, как
map
иfilter
иметь тестовую функцию в качестве первого аргумента).источник
Мне в основном нравится подход Андерса, так как он очень общий. Вот версия, которая ставит классификатор на первое место (для соответствия синтаксису фильтра) и использует defaultdict (предполагается, импортированный).
источник
Сначала (предварительное редактирование): Используйте наборы:
Это хорошо как для читабельности (IMHO), так и для производительности.
Второй ход (после OP-редактирования):
Создайте список хороших расширений в виде набора:
и это повысит производительность. В противном случае, то, что у вас есть, выглядит хорошо для меня.
источник
goodvals
.itertools.groupby почти делает то, что вы хотите, за исключением того, что требуется сортировка элементов, чтобы обеспечить получение единого непрерывного диапазона, поэтому сначала вам нужно отсортировать по ключу (в противном случае вы получите несколько групп с чередованием для каждого типа). например.
дает:
Подобно другим решениям, ключевая функция может быть определена для разделения на любое количество групп, которые вы хотите.
источник
Этот элегантный и лаконичный ответ @dansalmo обнаружился в комментариях, поэтому я просто разместил его здесь в качестве ответа, чтобы он мог получить известность, которую он заслуживает, особенно для новых читателей.
Полный пример:
источник
Если вы хотите сделать это в стиле FP:
Не самое читаемое решение, но, по крайней мере, перебирает mylist только один раз.
источник
Лично мне нравится версия, которую вы цитировали, при условии, что у вас уже есть список
goodvals
. Если нет, то что-то вроде:Конечно, это действительно очень похоже на использование понимания списка, как вы делали изначально, но с функцией вместо поиска:
В целом, я нахожу эстетику списочного понимания очень приятной. Конечно, если вы на самом деле не нужно , чтобы сохранить порядок и не нужны дубликаты, используя
intersection
иdifference
методы на множествах будет хорошо работать тоже.источник
filter(lambda x: is_good(x), mylist)
может быть уменьшен доfilter(is_good, mylist)
Я думаю, что обобщение разбиения итерируемого на основе N условий удобно
Например:
Если элемент может удовлетворять нескольким условиям, удалите разрыв.
источник
Проверьте это
источник
Иногда кажется, что понимание списка - не лучшая вещь для использования!
Я сделал небольшой тест, основанный на ответе людей на эту тему, протестированном по случайному списку. Вот генерация списка (возможно, есть лучший способ сделать это, но это не главное):
И здесь мы идем
Используя функцию cmpthese , лучший результат - ответ dbr:
источник
Еще одно решение этой проблемы. Мне нужно было решение как можно быстрее. Это означает только одну итерацию по списку и предпочтительно O (1) для добавления данных в один из результирующих списков. Это очень похоже на решение, предоставляемое састанином , за исключением гораздо более короткого:
Затем вы можете использовать функцию следующим образом:
Если вы не в порядке с результирующим
deque
объектом, вы можете легко преобразовать егоlist
,set
все, что вы , как (например ,list(lower)
). Преобразование происходит намного быстрее, чем построение списков напрямую.Этот метод сохраняет порядок элементов, а также любые дубликаты.
источник
Например, разделение списка на четные и нечетные
Или вообще:
Преимущества:
Недостатки
источник
Вдохновленный отличным (но кратким!) Ответом @ gnibbler , мы можем применить этот подход для сопоставления с несколькими разделами:
Тогда
splitter
может быть использовано следующее:Это работает для более чем двух разделов с более сложным отображением (и на итераторах тоже):
Или используя словарь для сопоставления:
источник
append возвращает None, так что это работает.
источник
Для исполнения попробуйте
itertools
.Смотрите itertools.ifilter или imap.
источник
[x for x in a if x > 50000]
на простом массиве из 100000 целых чисел (через random.shuffle),filter(lambda x: x> 50000, a)
потребуется 2 раза больше,ifilter(lambda x: x> 50000, a); list(result)
занимает примерно в 2,3 раза длиннее. Удивительно, но факт.Иногда вам не понадобится эта другая половина списка. Например:
источник
Это самый быстрый способ.
Он использует
if else
(как ответ dbr), но сначала создает набор. Набор уменьшает количество операций с O (m * n) до O (log m) + O (n), что приводит к увеличению скорости на 45% +.Немного короче:
Результаты тестов:
Полный тестовый код для Python 3.7 (модифицированный из FunkySayu):
источник
Если вы настаиваете на умном, вы могли бы принять решение Виндена и немного поддельную хитрость:
источник
Здесь уже довольно много решений, но еще один способ сделать это -
Итерирует по списку только один раз, и выглядит немного более питоническим и, следовательно, читаемым для меня.
источник
Я бы выбрал двухпроходный подход, отделяющий оценку предиката от фильтрации списка:
Что хорошо в этом, с точки зрения производительности (в дополнение к оценке
pred
только один раз для каждого членаiterable
), это то, что он перемещает много логики из интерпретатора в высоко оптимизированный код итерации и отображения. Это может ускорить итерацию на длинных итерациях, как описано в этом ответе .В плане выразительности он использует такие выразительные идиомы, как понимание и отображение.
источник
решение
тест
источник
Если вы не возражаете против использования внешней библиотеки, я знаю, что наивно реализовать эту операцию:
iteration_utilities.partition
:more_itertools.partition
источник
Не уверен, что это хороший подход, но это можно сделать и так
источник
Если список состоит из групп и прерывистых разделителей, вы можете использовать:
Использование:
источник
Приятно, когда условие дольше, как в вашем примере. Читатель не должен выяснять отрицательное условие и охватывает ли он все другие случаи.
источник
Еще один ответ, короткий, но «злой» (для побочных эффектов понимания списка).
источник