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]
 Anuvrat Parashar21 июн. 2012 г., 15:27
приземлился здесь, ища способ иметь условие в выражении строителя множеств, ваш вопрос ответил на мой вопрос :)
 Xiong Chiamiov10 окт. 2016 г., 21:13
обсуждение той же темы в списке Python.
 Stew17 дек. 2015 г., 17:24
Трещина Это неудачное описание этой операции, поскольку она уже имеет конкретное значение по отношению к строкам Python. думаюделить более точное (или, по крайней мере, менее перегруженное в контексте итераций Python) слово для описания этой операции. Я приземлился здесь в поисках списка эквивалентовstr.split(), чтобыТрещина список в упорядоченную коллекцию последовательных подсписков. Например.split([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6])в отличие отразделительный элементы списка по категориям.
 ChaimG05 мар. 2017 г., 18:58
IMAGE_TYPES должен быть набором вместо кортежа:IMAGE_TYPES = set('.jpg','.jpeg','.gif','.bmp','.png'), n (1) вместо n (o / 2), практически без разницы в читаемости.

Ответы на вопрос(20)

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)

это

good, bad = [], []
for x in mylist:
    (bad, good)[x in goodvals].append(x)
 dansalmo31 мая 2013 г., 01:49
good.append(x) if x in goodvals else bad.append(x) более читабельно
 Rabih Kodeih27 сент. 2016 г., 22:37
Этот код, хотя и остроумный, не читается.
 yo'13 февр. 2014 г., 14:37
@dansalmo Тем более, что вы можете сделать его однострочным с циклом for, и если вы хотите добавить что-то более сложное, чемxВы можете сделать это в одинappend только:for x in mylist: (good if isgood(x) else bad).append(x)
 jgpaiva11 апр. 2013 г., 13:11
Это невероятно гениально! Мне потребовалось некоторое время, чтобы понять, что происходит, хотя. Я хотел бы знать, если другие думают, что это можно считать REA, код Dable или нет.
 javadba03 июн. 2015 г., 23:08
@dansalmo "более читабельно". Да. Но не веселее.

что понимание списка - не лучшая вещь для использования!

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

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%  --

вы могли бы принять решение Уиндена и немного поддельную хитрость:

def splay(l, f, d=None):
  d = d or {}
  for x in l: d.setdefault(f(x), []).append(x)
  return d
 Brian04 июн. 2009 г., 15:20
"D or {}" немного опасно. Если пустое слово будет передано, оно не будет видоизменено.
 Anders Eurenius05 июн. 2009 г., 09:19
Правда, но это возвращается, так что ... На самом деле, это прекрасный пример того, почему выне хочу добавить более умный к вашему коду. :-П

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

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

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

 winden04 июн. 2009 г., 21:46
ИМХО, если вы знаете способ сделать это с меньшим использованием процессора (и, следовательно, с меньшим энергопотреблением), нет никаких причин не использовать его.
 Matthew Flaschen04 июн. 2009 г., 10:32
Я согласен. Я искал «элегантный» (то есть здесь означает «короткий» и «встроенный / неявный») способ сделать это без двойного сканирования списка, но, похоже, (без профилирования) это путь. Конечно, это будет иметь значение только для больших объемов данных.
 Elliot Cameron27 апр. 2016 г., 23:52
@winden ... Портирование всего моего Python на C.;)

но еще один способ сделать это -

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')]
>>>

так как он очень общий. Вот версия, которая ставит классификатор на первое место (для соответствия синтаксису фильтра) и использует 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
 jpmc2624 окт. 2014 г., 20:49
Я собирался попытаться выбрать утверждения изДзен питона это применимо здесь, но это слишком много для комментария. =) Потрясающий кусок кода.

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 иметь тестовую функцию в качестве первого аргумента).

Сначала иди (pre-OP-edit): использовать наборы:

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'])

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

 Daniyar04 июн. 2009 г., 09:48
не лучшее решение, если списки были в некотором порядке до разделения, и вам нужно, чтобы они оставались в таком порядке.
 mavnn04 июн. 2009 г., 09:48
Разве это не удалит дубликаты?
 dash-tom-bang10 янв. 2013 г., 23:32
Создание набора - это O (n log n). Повторение списка дважды - O (n). Заданное решение может быть более элегантным (если оно правильное в первую очередь), но, безусловно, медленнее при увеличении n.

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

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']
 dbr04 июн. 2009 г., 15:04
Это много кода, чтобы заменить два списка понимания ...
 John La Rooy08 авг. 2014 г., 06:51
Обратите внимание, чтоtee сохраняет все значения между итераторами, которые он возвращает, поэтому он не будет экономить память, если вы зациклите один генератор, а затем другой.
 Ants Aasma04 июн. 2009 г., 17:33
Вы, вероятно, правы, что это нарушает принцип ЯГНИ. Он основан на предположении, что в будущем число различных списков, на которые можно разделить объекты, будет расти.
 dash-tom-bang10 янв. 2013 г., 23:29
Это может быть много кода, но если[ x for x in my_list if ExpensiveOperation(x) ] бегать долго, вы точно не хотите делать это дважды!
 cod3monk3y16 нояб. 2013 г., 22:08
+1 за предложение нескольких вариантов, в том числе на основе итераторов и конкретного решения "в X". OP в «goodvals» может быть небольшим, но замена его на очень большой словарь или дорогой предикат может быть дорогой. Кроме того, это уменьшает необходимость писать список понимания в два разавезде это необходимо, тем самым уменьшая вероятность введения опечаток / ошибки пользователя. Хорошее решение. Спасибо!

ее. Это означает только одну итерацию по списку и предпочтительно O (1) для добавления данных в один из результирующих списков. Это очень похоже на решение, предоставленноеsastaninкроме значительно короче

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)). Преобразование происходит намного быстрее, чем построение списков напрямую.

Этот метод сохраняет порядок элементов, а также любые дубликаты.

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

    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')]

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

itertools.

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

Видетьitertools.ifilter или имап.

itertools.ifilter (предикат, повторяемый)

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

 Corley Brigman13 янв. 2015 г., 14:12
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 раза больше. Странно, но это правда.

которую вы цитировали, если у вас уже есть список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 методы на множествах тоже будут хорошо работать.

 robru07 нояб. 2014 г., 02:26
Конечно,filter(lambda x: is_good(x), mylist) может быть уменьшен доfilter(is_good, mylist)
 Corley Brigman13 янв. 2015 г., 14:09
добавление дополнительного вызова функции фактически удваивает (!) время выполнения, по сравнению со списками, из того, что я видел в профилировании. в большинстве случаев трудно превзойти понимание списка.

что обобщение разбиения итерируемого на основе 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]

Если элемент может удовлетворять нескольким условиям, удалите разрыв.

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

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

 rlat22 июл. 2015 г., 13:34
Хотя он выполняет итерацию по списку только один раз, производительность не так хороша из-за добавления списка. Добавление к списку является потенциально дорогостоящей операцией (например, по сравнению с deque.append). На самом деле, это решение очень медленное по сравнению с другими решениями здесь (21,4 с на 100000 случайных целых чисел и проверка их значения).

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 '')

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

def part_with_predicate(l, pred):
    return [i for i in l if pred(i)], [i for i in l if not pred(i)]

Это не ленивый подход, и он повторяется дважды по списку, но он позволяет разбить список на одну строку кода.

 dash-tom-bang10 янв. 2013 г., 23:36
Похоже, две строки кода для меня. Если вы имеете в виду, что на сайте вызовов есть только одна строка, это не изменится, если разделить последнюю строку выше на две. Проблема заключается в том, что при выполненииpred(i) занимает много времени, вы удваиваете свое ожидание.
Решение Вопроса
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]

Опять же этоотлично!

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

На самом деле, я могу сделать еще один шаг назад и просто использовать простой цикл 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
 Antimony09 мая 2013 г., 20:03
Проблема в том, что это нарушает принцип СУХОГО. Было бы хорошо, если бы был лучший способ сделать это.
 balki21 июл. 2012 г., 17:42
Разве нет способа понимания списка без необходимости повторения списка дважды?
 user346734914 июн. 2015 г., 19:10
Этот простой цикл for должен быть встроенной функцией, побуждающей людей бегать2 Перечень списков в очевидной задаче без единой причины, кроме отсутствия в Python функции для этого, является ужасной идеей imho.
 leftaroundabout30 сент. 2015 г., 22:04
@TomaszGandor FTR, Хаскеллстаршая чем Python (и фактически повлиял на его дизайн). Я думаю, что синтаксис для понимания списков и лямбда-кодов был намеренно сохранен на многословной стороне, возможно, чтобы воспрепятствовать их чрезмерному использованию. Что действительно немного рискованно ... насколько мне нравится Haskell, я понимаю, почему многие люди находят Python в целом более читабельным.
 Tomasz Gandor24 мая 2015 г., 15:52
Когда аппетит к функциональному программированию (Haskell) или функциональному стилю (LINQ) повышается, мы начинаем ощущать запах Python для его возраста -[x for x in blah if ...] - подробный,lambda неуклюжий и ограниченный ... Похоже, за рулем самой крутой машины с 1995 года сегодня. Не так, как тогда.

Вдохновленный @ gnibbler'sотличный (но краткий!) ответМы можем применить этот подход для сопоставления с несколькими разделами:

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'])]
 Josh Bode14 мар. 2013 г., 12:20
... только что заметил, что это в основном то же самое, что @ alan-isaac уже ответил.

Ваш ответ на вопрос