一尘不染

如何根据谓词拆分序列?

python

我经常碰到需要将一个序列分为满足和不满足给定谓词的两个元素子序列(保留原始的相对顺序)。

这个假设的“分割器”功能在运行中看起来像这样:

>>> data = map(str, range(14))
>>> pred = lambda i: int(i) % 3 == 2
>>> splitter(data, pred)
[('2', '5', '8', '11'), ('0', '1', '3', '4', '6', '7', '9', '10', '12', '13')]

我的问题是:

Python是否已经有标准/内置的方式来做到这一点?

此功能当然不难编写代码(请参阅下面的附录),但是由于多种原因,我宁愿使用标准/内置方法,也不愿使用自卷式方法。

谢谢!

附录:

到目前为止,我发现用于在Python中处理此任务的最佳标准函数是itertools.groupby。但是,要将其用于此特定任务,必须为每个列表成员调用两次谓词函数,我觉得这很愚蠢:

>>> import itertools as it
>>> [tuple(v[1]) for v in it.groupby(sorted(data, key=pred), key=pred)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]

(上面的最后一个输出与前面显示的所需输出不同,因为满足谓词的元素的子序列排在最后而不是排在最后,但这是很小的,并且在需要时很容易修复。)

可以避免对谓词的多余调用(基本上是通过“内联备忘录”完成的),但是我为此做的最好的准备工作与简单性相去甚远splitter(data, pred)

>>> first = lambda t: t[0]
>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data),
... key=first), key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]

顺便说一句,如果您不关心保留原始顺序,则sorted默认的排序顺序可以完成工作(因此key可以从sorted调用中省略该参数):

>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data)),
... key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]

阅读 190

收藏
2021-01-20

共1个答案

一尘不染

内置模块中more_itertools有一个名为的函数partition,该函数完全可以满足topicstarter的要求。

from more_itertools import partition

numbers = [1, 2, 3, 4, 5, 6, 7]
predicate_false, predicate_true = partition(lambda x: x % 2 == 0, numbers)

print(list(predicate_false), list(predicate_true))

结果是[1, 3, 5, 7] [2, 4, 6]

2021-01-20