Есть ли способ "разветкить" список по два на основе условия

Я видел образец, повторяемый пару раз в моем командном коде, он выглядит так

numbers = [1, 2, 3, 4]
even_numbers = [n for n in numbers if n % 2 == 0]
odd_numbers = [n for n in numbers if n % 2 != 0]

Мне было интересно, есть ли какая-нибудь функция где-нибудь (я огляделся, но не смог ее найти), что бы сделать что-то вроде этого

numbers = [1, 2, 3, 4]
even_numbers, odd_numbers = fork(numbers, lambda x: x % 2 == 0)

Итак, эта функция, которую я ищу, получит итерабельность и функцию, и вернет два списка, будут значения, которые соответствуют предоставленному условию, а другие - те, которые этого не сделали.

Есть ли что-то вокруг стандартной библиотеки python, которая достигает этого?

Ответ 1

Обычно я называю это sift, но partition тоже прекрасен.

Другая, безрезультатная реализация может быть

def sift(iterable, predicate):
    t = []
    f = []
    for value in iterable:
        (t if predicate(value) else f).append(value)
    return (t, f)

even, odd = sift([1, 2, 3, 4, 5], lambda x: x % 2 == 0)

EDIT: для немного более сложной реализации, которая примерно на 30% быстрее (на моей установке Python в любом случае):

def sift2(iterable, predicate):
    t = []
    f = []
    ta = t.append
    fa = f.append
    for value in iterable:
        (ta if predicate(value) else fa)(value)
    return (t, f)

Ответ 2

Вы можете использовать следующую функцию:

from itertools import filterfalse, tee

def fork(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    t1, t2 = tee(iterable)
    return list(filterfalse(pred, t1)), list(filter(pred, t2))

Источник: itertools

Ответ 3

Полный код, следующий за предложением @jonrsharpe.

import itertools

def fork(iterable):
    "Returns list of even, odd elements of a list"
    t1, t2 = itertools.tee(iterable)
    pred = lambda i: i % 2 == 0
    return list(filter(pred, t2)), list(itertools.filterfalse(pred, t1))

odd, even = fork([1,2,3,4,5])
print(odd)
print(even)

Альтернативная версия numpy, которая может быть быстрее для больших массивов

import numpy as np

def fork(iterable):
    "Returns array of even, odd elements of an array"
    iterable_array = np.asarray(iterable)
    mask = (iterable_array % 2 == 0)
    return iterable_array[~mask], iterable_array[mask]

Ответ 4

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

def myFunc(iterable, func):

    first  = [i for i in iterable if func(i)]
    second = [i for i in iterable if not func(i)]

    return first,second


numbers = [1, 2, 3, 4]
even_numbers, odd_numbers = myFunc(numbers, lambda x: x % 2 == 0)

print(even_numbers) # [2, 4]
print(odd_numbers)  # [1, 3]

Ответ 5

Простейшая возможная реализация была бы, вероятно,

def partition(xs, cond):
    res = [], []
    for x in xs:
        res[not cond(x)].append(x)
    return res

Ответ 6

Вы можете создать свою собственную функцию:

l = [1, 2, 3, 4]
def fork(l,key):
   return list(filter(key,l)), [i for i in l if i not in list(filter(key,l))]
even_numbers, odd_numbers = fork(l, lambda x: x % 2 == 0)
print(even_numbers)
print(odd_numbers)

Выход:

[2, 4]
[1, 3]