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)