3

基于一个简单的列表理解:

yay = [ i for i in a if a[i] ]
nay = [ i for i in a if not a[i] ]

我想知道是否有一种方法可以同时分配yaynay值(即条件的命中和未命中)?

看起来像这样的东西

( yay , nay ) = ...

我对可读性和速度感到好奇(我有点惊讶地看到两个列表推导比附加到任一列表的单个 for 循环快 5% 左右)


更新:

最初的示例是在字典中获取“真”和“假”值键的列表......

a = {i: i >= 50 for i in range(100)}

yay = [k for k, v in a.items() if v]
nay = [k for k, v in a.items() if not v]
4

6 回答 6

7

这里通常的解决方案是不要全神贯注于使用列表理解的想法。只需使用一个for循环:

yay, nay = [], []
for i in a:
    if somecondition(i):
        yay.append(i)
    else:
        nay.append(i)

如果您发现自己经常这样做,那么只需将代码移出到一个函数中:

def yesno(seq, cond):
    yay, nay = [], []
    for i in seq:
        if cond(i):
            yay.append(i)
        else:
            nay.append(i)
    return yay, nay

yay, nay = yesno(a, lambda x: a[x])

评论表明这比列表理解要慢。将条件作为 lambda 传递不可避免地会产生很大的影响,我认为您对此无能为力,但一些性能影响可能来自查找append方法,并且可以改进:

def yesno(seq, cond):
    yay, nay = [], []
    yes, no = yay.append, nay.append
    for i in seq:
        if cond(i):
            yes(i)
        else:
            no(i)
    return yay, nay

我不知道这是否有很大的不同,但计时可能会很有趣。

在评论中@martineau 建议使用生成器并将其与any(). 我将在此处包含它,但我将替换any为 itertools 配方以使用迭代器:

def consume(iterator, n):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

然后你可以写:

yay, nay = [], []
consume((yay if a[i] else nay).append(i) for i in a)
于 2013-02-11T19:10:07.053 回答
4

我仍然会说,您的做法更具可读性,应该是建议的方法,但无论如何,如果您正在寻找替代方案,您可以期待 itertools 的解决方案

>>> from itertools import compress, imap
>>> from operator import not_
>>> yay, nay = compress(a,a.values()), compress(a, imap(not_,a.values()))
于 2013-02-11T19:17:15.950 回答
0

这可以通过以下方式完成:

yay, nay = zip(*[(k, None) if v else (None, k) for k, v in a.items()])
yay, nay = filter(None, yay), filter(None, nay)

至于它是否会更快......也许对于大量列表。否则,这可能无关紧要。

自然,如果None是您列表中的一个值,您将需要将其换成另一个哨兵并使用 进行身份检查filter()

于 2013-02-11T19:05:58.457 回答
0

您也许可以使用字典推导,但我有理由确定您不能*使用列表推导来完成您的要求。假设数据是或可以排序**我可能会使用itertools.groupby.

results = itertools.groupby(sorted_a, bool)

*资格:好的,Lattyware 的回答表明您可以None,但它也会为可迭代的每个成员生成一个具有值的元组。IMO 那是很多浪费。虽然我承认我什至没有考虑到这一点,但我并不为我没有感到羞耻。

**已排序:它需要按与分组依据相同的键进行排序。

于 2013-02-11T19:06:09.557 回答
0

编辑

哦,好吧,我写了一个与 Duncan 的一个非常相似的解决方案。所以我删除了我写的东西,我让我认为是最好的解决方案,将一个 Duncan 的解决方案和 martineau 的建议混合在一起(在我看来,使用any()比使用list()或列表更可取)像我写的那样理解; any()是个好主意,这比导入consume() IMO 的复杂性要好)

def disting(L):
        dust,right = [],[]
        dustapp = dust.append
        rightapp = right.append
        any(rightapp(x) if x else dustapp(x) for x in L)
        return right,dust

for seq in ((10,None,'a',0,None,45,'qada',False,True,0,456),
            [False,0,None,104,True,str,'',88,'AA',__name__]):
    yay,nay = disting(seq)     
    print 'seq == %r\nyay == %r\nnay == %r' % (seq,yay,nay)
    print '---------------------------------------'

结果

seq == (10, None, 'a', 0, None, 45, 'qada', False, True, 0, 456)
yay == [10, 'a', 45, 'qada', True, 456]
nay == [None, 0, None, False, 0]
---------------------------------------
seq == [False, 0, None, 104, True, <type 'str'>, '', 88, 'AA', '__main__']
yay == [104, True, <type 'str'>, 88, 'AA', '__main__']
nay == [False, 0, None, '']
---------------------------------------

顺便说一句,使用any()是有效的,因为rightapp(x)dustapp(x)返回 None。如果返回 True 或等价于 True,则any()内的迭代将停止!

于 2013-02-12T18:57:44.997 回答
0

它不漂亮,但你可以在这几行中做一些事情:

nay = []
yay = [foo for foo in foos if condition or nay.append(foo)]

这利用了or操作员的短路。

于 2013-02-11T22:23:17.273 回答