TL; DR
Ответ , получивший наибольшее количество голосов [1] Марк Байерс
def partition(pred, iterable):
trues = []
falses = []
for item in iterable:
if pred(item):
trues.append(item)
else:
falses.append(item)
return trues, falses
является самым простым и
быстрый.
Сравнительный анализ различных подходов
Различные предложенные подходы можно классифицировать
в целом в трех категориях,
- прямое манипулирование списком с помощью
lis.append
, возвращающее 2-кортеж
списков,
lis.append
опосредовано функциональным подходом, возвращая 2-кортеж
списков,
- по каноническому рецепту, указанному в штрафе
itertools
документация, возвращающая 2 кортежа, свободно говоря, генераторов.
Здесь следует ванильная реализация трех техник, сначала
функциональный подход, то itertools
и в итоге два разных
реализации прямого манипулирования списком, альтернатива
использование False
- ноль, True
- один трюк.
Обратите внимание, что это Python3 - следовательно, reduce
происходит от functools
-
и этот OP запросить кортеж типа (positives, negatives)
, но мой
реализации все возвращают (negatives, positives)
…
$ ipython
Python 3.6.2 |Continuum Analytics, Inc.| (default, Jul 20 2017, 13:51:32)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import functools
...:
...: def partition_fu(p, l, r=functools.reduce):
...: return r(lambda x, y: x[p(y)].append(y) or x, l, ([], []))
...:
In [2]: import itertools
...:
...: def partition_it(pred, iterable,
...: filterfalse=itertools.filterfalse,
...: tee=itertools.tee):
...: t1, t2 = tee(iterable)
...: return filterfalse(pred, t1), filter(pred, t2)
...:
In [3]: def partition_li(p, l):
...: a, b = [], []
...: for n in l:
...: if p(n):
...: b.append(n)
...: else:
...: a.append(n)
...: return a, b
...:
In [4]: def partition_li_alt(p, l):
...: x = [], []
...: for n in l: x[p(n)].append(n)
...: return x
...:
Нам нужен предикат для применения к нашим спискам и спискам (опять же, свободно
говоря) на котором оперировать.
In [5]: p = lambda n:n%2
In [6]: five, ten = range(50000), range(100000)
Чтобы преодолеть проблему при тестировании подхода itertools
, это было
сообщено joeln в
31 октября 2013 года в 6:17
Ерунда. Вы рассчитали время, необходимое для создания
генераторы в filterfalse
и filter
, но вы не повторяли
через вход или вызывается pred
один раз! Преимущество
itertools
рецепт состоит в том, что он не материализует ни один список, или выглядит
дальше впереди на входе, чем необходимо. Он вызывает pred
в два раза
часто и занимает почти вдвое дольше, чем Байерс и др.
Я думал о пустом цикле, который просто создает все пары
элементов в двух итерациях, возвращаемых другим разделом
функции.
Сначала мы используем два фиксированных списка, чтобы иметь представление о
подразумевается перегрузка (используя очень удобную магию IPython %timeit
)
In [7]: %timeit for e, o in zip(five, five): pass
4.21 ms ± 39.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Далее мы используем разные реализации, одну за другой
In [8]: %timeit for e, o in zip(*partition_fu(p, ten)): pass
53.9 ms ± 112 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [9]: %timeit for e, o in zip(*partition_it(p, ten)): pass
44.5 ms ± 3.84 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [10]: %timeit for e, o in zip(*partition_li(p, ten)): pass
36.3 ms ± 101 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [11]: %timeit for e, o in zip(*partition_li_alt(p, ten)): pass
37.3 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [12]:
Комментарии
Самый простой из подходов также самый быстрый.
Использование трюка x[p(n)]
бесполезно, потому что на каждом шагу вы
нужно индексировать структуру данных, давая вам незначительное штраф - это
Однако приятно знать, если вы хотите убедить выжившего в снижении
культура при питонировании.
Функциональный подход, который оперативно эквивалентен
альтернативная реализация append
, на ~ 50% медленнее, возможно, из-за
тот факт, что у нас есть дополнительная функция (w / r для оценки предиката)
вызов для каждого элемента списка.
Подход itertools
имеет (обычные) преимущества, которых нет
создается потенциально большой список, и input входной список не
полностью обработан, если вы выйдете из потребительской петли, но когда мы
использовать его медленнее из-за необходимости применять предикат на обоих
концы tee
Помимо
Я влюбился в object.mutate() or object
идиому, которая
был разоблачен Марией
в их ответ показ
функциональный подход к проблеме - боюсь, что рано или поздно
Я собираюсь злоупотреблять этим.
Сноска
[1] Принят и получил наибольшее количество голосов по состоянию на сегодня, 14 сентября 2017 года - но, конечно, я возлагаю на него самые большие надежды!