Python: разделить список на основе условия?

Я столкнулся с той же проблемой, и я попробовал все вышеперечисленные ответы. Но, к сожалению, ни одно из вышеизложенного не работало.

В качестве примечания я, наконец, решу это по pip uninstall distribute.

244
задан Parand 3 June 2009 в 21:08
поделиться

12 ответов

 good = [x для x в mylist, если x в хорошем состоянии]
bad = [x вместо x в моем списке, если x не в хороших пределах]

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

Этот код отлично читается и предельно ясен!

# 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]

Опять же, это нормально!

Могут быть небольшие улучшения производительности с использованием наборов, но это тривиальная разница, и я считаю, что понимание списка намного проще для чтения, и вам не нужно беспокоиться о нарушении порядка, удалении дубликатов и т. д.

На самом деле, я могу пойти еще дальше. "назад", и просто используйте простой цикл for:

images, anims = [], []

for f in files:
    if f.lower() in IMAGE_TYPES:
        images.append(f)
    else:
        anims.append(f)

Понимание списка или использование set () нормально, пока вам не понадобится добавить какую-то другую проверку или другую логику - скажем, вы хотите чтобы удалить все 0-байтовые jpeg, вы просто добавляете что-то вроде ..

if f[1] == 0:
    continue
103
ответ дан 23 November 2019 в 03:07
поделиться
bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]

добавляют не, возвращает Ни один, таким образом, это работает.

1
ответ дан 23 November 2019 в 03:07
поделиться
good.append(x) if x in goodvals else bad.append(x)

Этот изящный и краткий ответ @dansalmo обнаружился проложенный под землей в комментариях, таким образом, я просто повторно отправляю его здесь как ответ, таким образом, это может получить выдающееся положение, которого он заслуживает, специально для новых читателей.

Полный пример:

good, bad = [], []
for x in my_list:
    good.append(x) if x in goodvals else bad.append(x)
2
ответ дан 23 November 2019 в 03:07
поделиться

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

def splay(l, f, d=None):
  d = d or {}
  for x in l: d.setdefault(f(x), []).append(x)
  return d
0
ответ дан 23 November 2019 в 03:07
поделиться

Для повышения производительности попробуйте itertools .

Модуль itertools стандартизирует основной набор быстрых, эффективных с точки зрения памяти инструментов, которые полезны сами по себе или в сочетании. Вместе они образуют «алгебру итераторов», позволяющую кратко и эффективно создавать специализированные инструменты на чистом Python.

См. itertools.ifilter или imap.

itertools.ifilter (предикат, итерабельность)

Создайте итератор, который фильтрует элементы из итерируемых, возвращая только те, для которых предикат равен True

1
ответ дан 23 November 2019 в 03:07
поделиться

Первый шаг (предварительное редактирование): Используйте наборы:

mylist = [1,2,3,4,5,6,7]
goodvals = [1,3,7,8,9]

myset = set(mylist)
goodset = set(goodvals)

print list(myset.intersection(goodset))  # [1, 3, 7]
print list(myset.difference(goodset))    # [2, 4, 5, 6]

Это хорошо как для читабельности (ИМХО), так и для производительности.

Второй вариант (сообщение -OP-edit):

Создайте свой список хороших расширений в виде набора:

IMAGE_TYPES = set(['.jpg','.jpeg','.gif','.bmp','.png'])

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

13
ответ дан 23 November 2019 в 03:07
поделиться

Лично мне нравится версия, которую вы процитировали, если предположить, что у вас уже есть список добродетелей . Если нет, то что-то вроде:

good = filter(lambda x: is_good(x), mylist)
bad = filter(lambda x: not is_good(x), mylist)

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

good = [x for x in mylist if is_good(x)]
bad  = [x for x in mylist if not is_good(x)]

В общем, я считаю, что эстетика понимания списка очень приятно. Конечно, если вам на самом деле не нужно сохранять порядок и не нужны дубликаты, использование методов пересечения и разницы на множествах также будет работать.

5
ответ дан 23 November 2019 в 03:07
поделиться

Проблема со всеми предлагаемыми решениями заключается в том, что они будут сканировать и применять функцию фильтрации дважды. Я бы сделал простую небольшую функцию вроде этой:

def SplitIntoTwoLists(l, f):
  a = []
  b = []
  for i in l:
    if f(i):
      a.append(i)
    else:
      b.append(i)
 return (a,b)

Таким образом, вы ничего не обрабатываете дважды, а также не повторяете код.

24
ответ дан 23 November 2019 в 03:07
поделиться

itertools.groupby почти делает то, что вы хотите, за исключением того, что требует сортировки элементов, чтобы гарантировать получение единого непрерывного диапазона, поэтому вам нужно сначала отсортировать по ключу ( иначе вы получите несколько чередующихся групп для каждого типа). например,

def is_good(f):
    return f[2].lower() in IMAGE_TYPES

files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file3.gif', 123L, '.gif')]

for key, group in itertools.groupby(sorted(files, key=is_good), key=is_good):
    print key, list(group)

дает:

False [('file2.avi', 999L, '.avi')]
True [('file1.jpg', 33L, '.jpg'), ('file3.gif', 123L, '.gif')]

Подобно другим решениям, ключевую функцию можно определить для разделения на любое количество групп по вашему желанию.

9
ответ дан 23 November 2019 в 03:07
поделиться

Вот подход с ленивым итератором:

from itertools import tee

def split_on_condition(seq, condition):
    l1, l2 = tee((condition(item), item) for item in seq)
    return (i for p, i in l1 if p), (i for p, i in l2 if not p)

Он оценивает условие один раз для каждого элемента и возвращает два генератора, первый из которых возвращает значения из последовательности, в которой условие истинно, а другой - ложно.

Поскольку это лениво, вы можете использовать его на любом итераторе, даже на бесконечном:

from itertools import count, islice

def is_prime(n):
    return n > 1 and all(n % i for i in xrange(2, n))

primes, not_primes = split_on_condition(count(), is_prime)
print("First 10 primes", list(islice(primes, 10)))
print("First 10 non-primes", list(islice(not_primes, 10)))

Обычно, хотя подход с возвратом неленивых списков лучше:

def split_on_condition(seq, condition):
    a, b = [], []
    for item in seq:
        (a if condition(item) else b).append(item)
    return a, b

Изменить: Для вашего более конкретного варианта использования разделения элементов на разные списки с помощью какой-то ключ, вот общая функция, которая делает это:

DROP_VALUE = lambda _:_
def split_by_key(seq, resultmapping, keyfunc, default=DROP_VALUE):
    """Split a sequence into lists based on a key function.

        seq - input sequence
        resultmapping - a dictionary that maps from target lists to keys that go to that list
        keyfunc - function to calculate the key of an input value
        default - the target where items that don't have a corresponding key go, by default they are dropped
    """
    result_lists = dict((key, []) for key in resultmapping)
    appenders = dict((key, result_lists[target].append) for target, keys in resultmapping.items() for key in keys)

    if default is not DROP_VALUE:
        result_lists.setdefault(default, [])
        default_action = result_lists[default].append
    else:
        default_action = DROP_VALUE

    for item in seq:
        appenders.get(keyfunc(item), default_action)(item)

    return result_lists

Использование:

def file_extension(f):
    return f[2].lower()

split_files = split_by_key(files, {'images': IMAGE_TYPES}, keyfunc=file_extension, default='anims')
print split_files['images']
print split_files['anims']
100
ответ дан 23 November 2019 в 03:07
поделиться

Если вы хотите сделать его в стиле FP:

good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
                                        for y in mylist)) ]

Не самое читабельное решение, но, по крайней мере, итерация по mylist выполняется только один раз.

5
ответ дан 23 November 2019 в 03:07
поделиться

Мне в основном нравится подход Андерса, поскольку он очень общий. Вот версия, которая ставит категоризатор на первое место (чтобы соответствовать синтаксису фильтра) и использует defaultdict (предполагается, что он импортирован).

def categorize(func, seq):
    """Return mapping from categories to lists
    of categorized items.
    """
    d = defaultdict(list)
    for item in seq:
        d[func(item)].append(item)
    return d
14
ответ дан 23 November 2019 в 03:07
поделиться