Is there a way to capture problems on an understanding list?

advertisements

Based on a simple list comprehension :

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

I am wondering if there is a way to assign both the yay and nay values at once ( ie hits and misses on the conditional )?

Something that would look like this

( yay , nay ) = ...

I was curious about this on readability and speed ( I was a bit surprised to see two list comprehensions are about 5% faster than a single for-loop that appends to either list )


update:

The original example was to grab a listing of "true" and "false" valued keys in a dict...

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]


The usual solution here is not to get all hung up on the idea of using a list comprehension. Just use a for loop:

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

If you find yourself doing this a lot then simply move the code out into a function:

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])

The comments suggest this is slower than list comprehension. Passing the condition as a lambda is inevitably going to have a big hit and I don't think you can do much about that, but some of the performance hit probably comes from looking up the append method and that can be improved:

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

I don't know if that makes much of a difference, but it might be interesting to time it.

In the comments @martineau suggests using a generator and consuming it with any(). I'll include that here, but I would replace any with the itertools recipe to consume an iterator:

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)

and then you can write:

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