Мой взгляд на это. Я предлагаю ленивую однопроходную функцию 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
иметь тестовую функцию в качестве первого аргумента).