Я столкнулся с той же проблемой, и я попробовал все вышеперечисленные ответы. Но, к сожалению, ни одно из вышеизложенного не работало.
В качестве примечания я, наконец, решу это по pip uninstall distribute
.
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
bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]
добавляют не, возвращает Ни один, таким образом, это работает.
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)
Если вы настаиваете на умном, вы можете воспользоваться решением Виндена и немного искусственным умом:
def splay(l, f, d=None):
d = d or {}
for x in l: d.setdefault(f(x), []).append(x)
return d
Для повышения производительности попробуйте itertools
.
Модуль itertools стандартизирует основной набор быстрых, эффективных с точки зрения памяти инструментов, которые полезны сами по себе или в сочетании. Вместе они образуют «алгебру итераторов», позволяющую кратко и эффективно создавать специализированные инструменты на чистом Python.
См. itertools.ifilter или imap.
itertools.ifilter (предикат, итерабельность)
Создайте итератор, который фильтрует элементы из итерируемых, возвращая только те, для которых предикат равен True
Первый шаг (предварительное редактирование): Используйте наборы:
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'])
, и это повысит производительность. В остальном то, что у вас есть, мне нравится.
Лично мне нравится версия, которую вы процитировали, если предположить, что у вас уже есть список добродетелей
. Если нет, то что-то вроде:
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)]
В общем, я считаю, что эстетика понимания списка очень приятно. Конечно, если вам на самом деле не нужно сохранять порядок и не нужны дубликаты, использование методов пересечения
и разницы
на множествах также будет работать.
Проблема со всеми предлагаемыми решениями заключается в том, что они будут сканировать и применять функцию фильтрации дважды. Я бы сделал простую небольшую функцию вроде этой:
def SplitIntoTwoLists(l, f):
a = []
b = []
for i in l:
if f(i):
a.append(i)
else:
b.append(i)
return (a,b)
Таким образом, вы ничего не обрабатываете дважды, а также не повторяете код.
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')]
Подобно другим решениям, ключевую функцию можно определить для разделения на любое количество групп по вашему желанию.
Вот подход с ленивым итератором:
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']
Если вы хотите сделать его в стиле FP:
good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
for y in mylist)) ]
Не самое читабельное решение, но, по крайней мере, итерация по mylist выполняется только один раз.
Мне в основном нравится подход Андерса, поскольку он очень общий. Вот версия, которая ставит категоризатор на первое место (чтобы соответствовать синтаксису фильтра) и использует 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