有没有办法捕捉列表理解中的失误?

时间:2013-02-11 19:02:32

标签: python

基于简单的列表理解:

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]

6 个答案:

答案 0 :(得分: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()一起使用。我将在这里包含,但我会用itertools配方替换any以使用迭代器:

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)

答案 1 :(得分:4)

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

>>> from itertools import compress, imap
>>> from operator import not_
>>> yay, nay = compress(a,a.values()), compress(a, imap(not_,a.values()))

答案 2 :(得分: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()进行身份检查。

答案 3 :(得分:0)

你可能能够使用词典理解,但我有理由相信你不能*使用列表理解来做你所要求的。假设数据是或可以排序**我可能会使用itertools.groupby

results = itertools.groupby(sorted_a, bool)

*资格:好的,Lattyware的答案显示你可以,但它也为iterable的每个成员生成一个None值的元组。 IMO认为这很浪费。虽然我承认我甚至没有考虑到这一点,但我并不感到羞耻,因为我没有。

**排序:需要按照分组的相同键进行排序。

答案 4 :(得分:0)

它不漂亮,但你可以在这些方面做点什么:

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

这利用了or运算符上的短路。

答案 5 :(得分: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)会返回无。如果返回True或等效于True, any()内的迭代将停止!