Python: разбивать список на основе условия?
Какой лучший способ, как с эстетической точки зрения, так и с точки зрения производительности, разбивать список элементов на несколько списков на основе условного? Эквивалент:
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]
Ответы
Ответ 1
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]
Опять же, это прекрасно!
Может быть небольшое улучшение производительности с использованием наборов, но это тривиальное различие, и я считаю, что понимание списка намного легче читать, и вам не нужно беспокоиться о том, что порядок запущен, дубликаты удаляются так же.
На самом деле, я могу пойти на другой шаг "назад" и просто использовать простой цикл:
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
Ответ 2
good, bad = [], []
for x in mylist:
(bad, good)[x in goodvals].append(x)
Ответ 3
Здесь применяется ленивый итератор:
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']
Ответ 4
Проблема со всеми предлагаемыми решениями заключается в том, что он будет сканировать и применять функцию фильтрации дважды. Я бы сделал небольшую небольшую функцию:
def SplitIntoTwoLists(l, f):
a = []
b = []
for i in l:
if f(i):
a.append(i)
else:
b.append(i)
return (a,b)
Таким образом, вы ничего не обрабатываете дважды, а также не повторяете код.
Ответ 5
Я принимаю это. Я предлагаю ленивую, однопроходную, partition
функцию,
который сохраняет относительный порядок в выходных подпоследовательностях.
1. Требования
Я предполагаю, что требования:
- поддерживать относительный порядок элементов (следовательно, нет множеств и
словари)
- оценивать условие только один раз для каждого элемента (следовательно, не используя
(
i
) filter
или groupby
)
- позволяют ленивое потребление любой последовательности (если мы можем позволить себе
прекоммутировать их, то наивная реализация, вероятно, будет
также приемлемо)
2. split
библиотека
Моя partition
функция (представлена ниже) и другие подобные функции
превратили его в небольшую библиотеку:
Он устанавливается обычно через PyPI:
pip install --user split
Чтобы разбить базу списка на условие, используйте функцию partition
:
>>> from split import partition
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi') ]
>>> image_types = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> images, other = partition(lambda f: f[-1] in image_types, files)
>>> list(images)
[('file1.jpg', 33L, '.jpg')]
>>> list(other)
[('file2.avi', 999L, '.avi')]
3. partition
объясняется функция
Внутри нам нужно построить сразу две подпоследовательности, поэтому потребление
только одна выходная последовательность заставит другую вычислить
слишком. И нам нужно сохранить состояние между запросами пользователей (обработано хранилище
но еще не запрошенные элементы). Чтобы сохранить состояние, я использую два двойных
очереди (deques
):
from collections import deque
SplitSeq
класс заботится о домашнем хозяйстве:
class SplitSeq:
def __init__(self, condition, sequence):
self.cond = condition
self.goods = deque([])
self.bads = deque([])
self.seq = iter(sequence)
Магия происходит в методе .getNext()
. Это почти похоже на .next()
итераторов, но позволяет указать, какой элемент мы хотим
в этот раз. За сценой он не отбрасывает отклоненные элементы,
но вместо этого помещает их в одну из двух очередей:
def getNext(self, getGood=True):
if getGood:
these, those, cond = self.goods, self.bads, self.cond
else:
these, those, cond = self.bads, self.goods, lambda x: not self.cond(x)
if these:
return these.popleft()
else:
while 1: # exit on StopIteration
n = self.seq.next()
if cond(n):
return n
else:
those.append(n)
Конечный пользователь должен использовать функцию partition
. Требуется
функция состояния и последовательность (точно так же, как map
или filter
), и
возвращает два генератора. Первый генератор строит подпоследовательность
элементы, для которых выполняется условие, вторая строит
комплементарная подпоследовательность. Итераторы и генераторы позволяют лениться
расщепление даже длинных или бесконечных последовательностей.
def partition(condition, sequence):
cond = condition if condition else bool # evaluate as bool if condition == None
ss = SplitSeq(cond, sequence)
def goods():
while 1:
yield ss.getNext(getGood=True)
def bads():
while 1:
yield ss.getNext(getGood=False)
return goods(), bads()
Я выбрал тестовую функцию первым аргументом для облегчения
частичное применение в будущем (подобно тому, как map
и filter
имеют тестовую функцию в качестве первого аргумента).
Ответ 6
Сначала перейдите (pre-OP-edit): Используйте наборы:
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]
Это хорошо для читаемости (IMHO) и производительности.
Второй переход (пост-OP-редактирование):
Создайте список хороших расширений как набор:
IMAGE_TYPES = set(['.jpg','.jpeg','.gif','.bmp','.png'])
что увеличит производительность. В противном случае, то, что у вас есть, выглядит хорошо для меня.
Ответ 7
Мне в основном нравится подход Андерса, поскольку он очень общий. Здесь версия, которая сначала ставит классификатор (чтобы соответствовать синтаксису фильтра), и использует 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
Ответ 8
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')]
Подобно другим решениям, функция funf может быть определена для разделения на любое количество желаемых групп.
Ответ 9
Лично мне нравится версия, которую вы указали, если у вас уже есть список goodvals
, висящий вокруг. Если нет, то вроде:
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)]
В целом, я считаю эстетику перечня понятий очень приятной. Конечно, если вам действительно не нужно сохранять порядок и не нужно дубликатов, использование методов intersection
и difference
на наборах тоже будет хорошо работать.
Ответ 10
Если вы хотите сделать это в стиле FP:
good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
for y in mylist)) ]
Не наиболее читаемое решение, но, по крайней мере, итерации через mylist только один раз.
Ответ 11
Я думаю, что обобщение расщепления итерабельного на основе N условий удобно
from collections import OrderedDict
def partition(iterable,*conditions):
'''Returns a list with the elements that satisfy each of condition.
Conditions are assumed to be exclusive'''
d= OrderedDict((i,list())for i in range(len(conditions)))
for e in iterable:
for i,condition in enumerate(conditions):
if condition(e):
d[i].append(e)
break
return d.values()
Например:
ints,floats,other = partition([2, 3.14, 1, 1.69, [], None],
lambda x: isinstance(x, int),
lambda x: isinstance(x, float),
lambda x: True)
print " ints: {}\n floats:{}\n other:{}".format(ints,floats,other)
ints: [2, 1]
floats:[3.14, 1.69]
other:[[], None]
Если элемент может удовлетворять нескольким условиям, удалите разрыв.
Ответ 12
def partition(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
Отметьте this
Ответ 13
Иногда, похоже, что понимание списков - это не лучшая вещь!
Я сделал небольшой тест, основанный на ответе, который люди дали этой теме, проверенном в произвольном сгенерированном списке. Вот генерация списка (возможно, лучший способ сделать это, но это не так):
good_list = ('.jpg','.jpeg','.gif','.bmp','.png')
import random
import string
my_origin_list = []
for i in xrange(10000):
fname = ''.join(random.choice(string.lowercase) for i in range(random.randrange(10)))
if random.getrandbits(1):
fext = random.choice(good_list)
else:
fext = "." + ''.join(random.choice(string.lowercase) for i in range(3))
my_origin_list.append((fname + fext, random.randrange(1000), fext))
И вот мы идем
# Parand
def f1():
return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]
# dbr
def f2():
a, b = list(), list()
for e in my_origin_list:
if e[2] in good_list:
a.append(e)
else:
b.append(e)
return a, b
# John La Rooy
def f3():
a, b = list(), list()
for e in my_origin_list:
(b, a)[e[2] in good_list].append(e)
return a, b
# Ants Aasma
def f4():
l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
return [i for p, i in l1 if p], [i for p, i in l2 if not p]
# My personal way to do
def f5():
a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
return list(filter(None, a)), list(filter(None, b))
# BJ Homer
def f6():
return filter(lambda e: e[2] in good_list, my_origin_list), filter(lambda e: not e[2] in good_list, my_origin_list)
Используя функцию cmpthese, лучшим результатом будет ответ dbr:
f1 204/s -- -5% -14% -15% -20% -26%
f6 215/s 6% -- -9% -11% -16% -22%
f3 237/s 16% 10% -- -2% -7% -14%
f4 240/s 18% 12% 2% -- -6% -13%
f5 255/s 25% 18% 8% 6% -- -8%
f2 277/s 36% 29% 17% 15% 9% --
Ответ 14
Еще одно решение этой проблемы. Мне нужно решение, которое будет как можно быстрее. Это означает только одну итерацию по списку и предпочтительно O (1) для добавления данных в один из результирующих списков. Это очень похоже на решение, предоставляемое састанином, за исключением гораздо более короткого:
from collections import deque
def split(iterable, function):
dq_true = deque()
dq_false = deque()
# deque - the fastest way to consume an iterator and append items
deque((
(dq_true if function(item) else dq_false).append(item) for item in iterable
), maxlen=0)
return dq_true, dq_false
Затем вы можете использовать эту функцию следующим образом:
lower, higher = split([0,1,2,3,4,5,6,7,8,9], lambda x: x < 5)
selected, other = split([0,1,2,3,4,5,6,7,8,9], lambda x: x in {0,4,9})
Если вы не в порядке с результирующим объектом deque
, вы можете легко преобразовать его в list
, set
, как вам нравится (например, list(lower)
). Преобразование происходит намного быстрее, чем прямое построение списков.
Эти методы сохраняют порядок элементов, а также любые дубликаты.
Ответ 15
Для обеспечения производительности попробуйте itertools
.
Модуль itertools стандартизирует базовый набор быстрых, эффективных по памяти инструментов, которые полезны сами по себе или в сочетании. Вместе они образуют "итерационную алгебру", что позволяет создавать специализированные инструменты лаконично и эффективно в чистом Python.
См. itertools.ifilter или imap.
itertools.ifilter(предикат, итерируемый)
Создайте итератор, который фильтрует элементы из итерируемого, возвращая только те, для которых предикат True
Ответ 16
Иногда вам не нужна эта другая половина списка.
Например:
import sys
from itertools import ifilter
trustedPeople = sys.argv[1].split(',')
newName = sys.argv[2]
myFriends = ifilter(lambda x: x.startswith('Shi'), trustedPeople)
print '%s is %smy friend.' % (newName, newName not in myFriends 'not ' or '')
Ответ 17
Вдохновленный замечательным (но кратким!) Ответом @gnibbler, мы можем применить этот подход для сопоставления с несколькими разделами:
from collections import defaultdict
def splitter(l, mapper):
"""Split an iterable into multiple partitions generated by a callable mapper."""
results = defaultdict(list)
for x in l:
results[mapper(x)] += [x]
return results
Тогда splitter
может быть использован следующим образом:
>>> l = [1, 2, 3, 4, 2, 3, 4, 5, 6, 4, 3, 2, 3]
>>> split = splitter(l, lambda x: x % 2 == 0) # partition l into odds and evens
>>> split.items()
>>> [(False, [1, 3, 3, 5, 3, 3]), (True, [2, 4, 2, 4, 6, 4, 2])]
Это работает для более чем двух разделов с более сложным отображением (и на итераторах тоже):
>>> import math
>>> l = xrange(1, 23)
>>> split = splitter(l, lambda x: int(math.log10(x) * 5))
>>> split.items()
[(0, [1]),
(1, [2]),
(2, [3]),
(3, [4, 5, 6]),
(4, [7, 8, 9]),
(5, [10, 11, 12, 13, 14, 15]),
(6, [16, 17, 18, 19, 20, 21, 22])]
Или используя словарь для сопоставления:
>>> map = {'A': 1, 'X': 2, 'B': 3, 'Y': 1, 'C': 2, 'Z': 3}
>>> l = ['A', 'B', 'C', 'C', 'X', 'Y', 'Z', 'A', 'Z']
>>> split = splitter(l, map.get)
>>> split.items()
(1, ['A', 'Y', 'A']), (2, ['C', 'C', 'X']), (3, ['B', 'Z', 'Z'])]
Ответ 18
Например, разбиение списка четным и нечетным
arr = range(20)
even, odd = reduce(lambda res, next: res[next % 2].append(next) or res, arr, ([], []))
Или вообще:
def split(predicate, iterable):
return reduce(lambda res, e: res[predicate(e)].append(e) or res, iterable, ([], []))
Преимущества:
- Самый короткий возможный путь
- Предикат применяется только один раз для каждого элемента
Недостатки
- Требуется знание парадигмы функционального программирования
Ответ 19
bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]
append возвращает None, так что это работает.
Ответ 20
Если вы настаиваете на умном, вы можете взять решение Winden и немного немного ложную умность:
def splay(l, f, d=None):
d = d or {}
for x in l: d.setdefault(f(x), []).append(x)
return d
Ответ 21
Здесь уже довольно много решений, но еще один способ сделать это -
anims = []
images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]
Итерирует по списку только один раз, и выглядит немного более питоническим и, следовательно, читаемым для меня.
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file1.bmp', 33L, '.bmp')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> anims = []
>>> images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]
>>> print '\n'.join([str(anims), str(images)])
[('file2.avi', 999L, '.avi')]
[('file1.jpg', 33L, '.jpg'), ('file1.bmp', 33L, '.bmp')]
>>>
Ответ 22
Я бы взял двухпроходный подход, отделяя оценку предиката от фильтрации списка:
def partition(pred, iterable):
xs = list(zip(map(pred, iterable), iterable))
return [x[1] for x in xs if x[0]], [x[1] for x in xs if not x[0]]
Что приятное в этом отношении, по производительности (помимо оценки pred
только один раз для каждого члена iterable
), заключается в том, что он перемещает много логики из интерпретатора и в оптимизированную итерацию и сопоставление код. Это может ускорить итерацию по длинным итерам, как описано в этом ответе.
Выразительность, он использует экспрессивные идиомы, такие как понимание и сопоставление.
Ответ 23
Решение
from itertools import tee
def unpack_args(fn):
return lambda t: fn(*t)
def separate(fn, lx):
return map(
unpack_args(
lambda i, ly: filter(
lambda el: bool(i) == fn(el),
ly)),
enumerate(tee(lx, 2)))
Тест
[even, odd] = separate(
lambda x: bool(x % 2),
[1, 2, 3, 4, 5])
print(list(even) == [2, 4])
print(list(odd) == [1, 3, 5])
Ответ 24
Если вы не возражаете использовать внешнюю библиотеку там два, я знаю, что nativly реализовать эту операцию:
>>> files = [ ('file1.jpg', 33, '.jpg'), ('file2.avi', 999, '.avi')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
-
iteration_utilities.partition
:
>>> from iteration_utilities import partition
>>> notimages, images = partition(files, lambda x: x[2].lower() in IMAGE_TYPES)
>>> notimages
[('file2.avi', 999, '.avi')]
>>> images
[('file1.jpg', 33, '.jpg')]
-
more_itertools.partition
>>> from more_itertools import partition
>>> notimages, images = partition(lambda x: x[2].lower() in IMAGE_TYPES, files)
>>> list(notimages) # returns a generator so you need to explicitly convert to list.
[('file2.avi', 999, '.avi')]
>>> list(images)
[('file1.jpg', 33, '.jpg')]
Ответ 25
Не уверен, что это хороший подход, но это можно сделать и таким образом
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi')]
images, anims = reduce(lambda (i, a), f: (i + [f], a) if f[2] in IMAGE_TYPES else (i, a + [f]), files, ([], []))
Ответ 26
Если список состоит из групп и прерывистых разделителей, вы можете использовать:
def split(items, p):
groups = [[]]
for i in items:
if p(i):
groups.append([])
groups[-1].append(i)
return groups
Использование:
split(range(1,11), lambda x: x % 3 == 0)
# gives [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
Ответ 27
Это самый быстрый способ.
Он использует if else
(как и ответ dbr), но сначала создает набор. Набор сокращает количество операций с O (m * n) до O (log m) + O (n), что приводит к увеличению скорости на 45% +.
good_list_set = set(good_list) # 45% faster than a tuple.
good, bad = [], []
for item in my_origin_list:
if item in good_list_set:
good.append(item)
else:
bad.append(item)
Немного короче:
good_list_set = set(good_list) # 45% faster than a tuple.
good, bad = [], []
for item in my_origin_list:
out = good if item in good_list_set else bad
out.append(item)
Результаты тестов:
filter_BJHomer 80/s -- -3265% -5312% -5900% -6262% -7273% -7363% -8051% -8162% -8244%
zip_Funky 118/s 4848% -- -3040% -3913% -4450% -5951% -6085% -7106% -7271% -7393%
two_lst_tuple_JohnLaRoy 170/s 11332% 4367% -- -1254% -2026% -4182% -4375% -5842% -6079% -6254%
if_else_DBR 195/s 14392% 6428% 1434% -- -882% -3348% -3568% -5246% -5516% -5717%
two_lst_compr_Parand 213/s 16750% 8016% 2540% 967% -- -2705% -2946% -4786% -5083% -5303%
if_else_1_line_DanSalmo 292/s 26668% 14696% 7189% 5033% 3707% -- -331% -2853% -3260% -3562%
tuple_if_else 302/s 27923% 15542% 7778% 5548% 4177% 343% -- -2609% -3029% -3341%
set_1_line 409/s 41308% 24556% 14053% 11035% 9181% 3993% 3529% -- -569% -991%
set_shorter 434/s 44401% 26640% 15503% 12303% 10337% 4836% 4345% 603% -- -448%
set_if_else 454/s 46952% 28358% 16699% 13349% 11290% 5532% 5018% 1100% 469% --
Полный тестовый код для Python 3.7 (модифицированный из FunkySayu):
good_list = ['.jpg','.jpeg','.gif','.bmp','.png']
import random
import string
my_origin_list = []
for i in range(10000):
fname = ''.join(random.choice(string.ascii_lowercase) for i in range(random.randrange(10)))
if random.getrandbits(1):
fext = random.choice(list(good_list))
else:
fext = "." + ''.join(random.choice(string.ascii_lowercase) for i in range(3))
my_origin_list.append((fname + fext, random.randrange(1000), fext))
# Parand
def two_lst_compr_Parand(*_):
return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]
# dbr
def if_else_DBR(*_):
a, b = list(), list()
for e in my_origin_list:
if e[2] in good_list:
a.append(e)
else:
b.append(e)
return a, b
# John La Rooy
def two_lst_tuple_JohnLaRoy(*_):
a, b = list(), list()
for e in my_origin_list:
(b, a)[e[2] in good_list].append(e)
return a, b
# # Ants Aasma
# def f4():
# l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
# return [i for p, i in l1 if p], [i for p, i in l2 if not p]
# My personal way to do
def zip_Funky(*_):
a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
return list(filter(None, a)), list(filter(None, b))
# BJ Homer
def filter_BJHomer(*_):
return list(filter(lambda e: e[2] in good_list, my_origin_list)), list(filter(lambda e: not e[2] in good_list, my_origin_list))
# ChaimG answer; as a list.
def if_else_1_line_DanSalmo(*_):
good, bad = [], []
for e in my_origin_list:
_ = good.append(e) if e[2] in good_list else bad.append(e)
return good, bad
# ChaimG answer; as a set.
def set_1_line(*_):
good_list_set = set(good_list)
good, bad = [], []
for e in my_origin_list:
_ = good.append(e) if e[2] in good_list_set else bad.append(e)
return good, bad
# ChaimG set and if else list.
def set_shorter(*_):
good_list_set = set(good_list)
good, bad = [], []
for e in my_origin_list:
out = good if e[2] in good_list_set else bad
out.append(e)
return good, bad
# ChaimG best answer; if else as a set.
def set_if_else(*_):
good_list_set = set(good_list)
good, bad = [], []
for e in my_origin_list:
if e[2] in good_list_set:
good.append(e)
else:
bad.append(e)
return good, bad
# ChaimG best answer; if else as a set.
def tuple_if_else(*_):
good_list_tuple = tuple(good_list)
good, bad = [], []
for e in my_origin_list:
if e[2] in good_list_tuple:
good.append(e)
else:
bad.append(e)
return good, bad
def cmpthese(n=0, functions=None):
results = {}
for func_name in functions:
args = ['%s(range(256))' % func_name, 'from __main__ import %s' % func_name]
t = Timer(*args)
results[func_name] = 1 / (t.timeit(number=n) / n) # passes/sec
functions_sorted = sorted(functions, key=results.__getitem__)
for f in functions_sorted:
diff = []
for func in functions_sorted:
if func == f:
diff.append("--")
else:
diff.append(f"{results[f]/results[func]*100 - 100:5.0%}")
diffs = " ".join(f'{x:>8s}' for x in diff)
print(f"{f:27s} \t{results[f]:,.0f}/s {diffs}")
if __name__=='__main__':
from timeit import Timer
cmpthese(1000, 'two_lst_compr_Parand if_else_DBR two_lst_tuple_JohnLaRoy zip_Funky filter_BJHomer if_else_1_line_DanSalmo set_1_line set_if_else tuple_if_else set_shorter'.split(" "))
Ответ 28
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims = [f for f in files if f not in images]
Приятно, когда условие длиннее, как в вашем примере. Читателю не нужно выяснять отрицательное условие и охватывать ли оно все остальные случаи.
Ответ 29
Еще один ответ, короткий, но "злой" (для побочных эффектов понимания списка).
digits = list(range(10))
odd = [x.pop(i) for i, x in enumerate(digits) if x % 2]
>>> odd
[1, 3, 5, 7, 9]
>>> digits
[0, 2, 4, 6, 8]
Ответ 30
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)