user1718064
user1718064

Reputation: 475

Embedded if statements

Suppose I have a function like the following:

bigrams=[(k,v) for (k,v) in dict_bigrams.items()
         if k[:pos_qu]==selection[:pos_qu]
         and (k[pos_qu+1:]==selection[pos_qu+1:] if pos_qu!=1)
         and k[pos_qu] not in alphabet.values()]

I want to make the second condition, namely k[pos_qu+1:]==selection[pos_qu+1:] dependent from another if statement, if pos_qu!=1. I tried (as shown above) by including the two together into parentheses but python flags a syntax error at the parentheses

Upvotes: 2

Views: 931

Answers (3)

abarnert
abarnert

Reputation: 365597

Whenever you find yourself with a complex list comprehension, trying to figure out how to do something complicated and not knowing how, the answer is usually to break things up. Expression syntax is inherently more limited than full statement (or multi-statement suite) syntax in Python, to prevent you from writing things that you won't be able to read later. Usually, that's a good thing—and, even when it isn't, you're better off going along with it than trying to fight it.

In this case, you've got a trivial comprehension, except for the if clause, which you don't know how to write as an expression. So, I'd turn the condition into a separate function:

def isMyKindOfKey(k):
    … condition here
[(k,v) for (k,v) in dict_bigrams.items() if isMyKindOfKey(k)]

This lets you use full multi-statement syntax for the condition. It also lets you give the condition a name (hopefully something better than isMyKindOfKey); makes the parameters, local values captured by the closure, etc. more explicit; lets you test the function separately or reuse it; etc.

In cases where the loop itself is the non-trivial part (or there's just lots of nesting), it usually makes more sense to break up the entire comprehension into an explicit for loop and append, but I don't think that's necessary here.

It's worth noting that in this case—as in general—this doesn't magically solve your problem, it just gives you more flexibility in doing so. For example, you can use the same transformation from postfix if to infix or that F.J suggests, but you can also leave it as an if, e.g., like this:

def isMyKindOfKey(k):
    retval = k[:pos_qu]==selection[:pos_qu]
    if pos_qu!=1:
        retval = retval and (k[pos_qu+1:]==selection[pos_qu+1:])
    retval = retval and (k[pos_qu] not in alphabet.values())
    return retval

That probably isn't actually the way I'd write this, but you can see how this is a trivial way to transform what's in your head into code, which would be very hard to do in an expression.

Upvotes: 2

Andrew Clark
Andrew Clark

Reputation: 208405

If I understand your requirement correctly, you only want to check k[pos_qu+1:]==selection[pos_qu+1:] if the condition pos_qu!=1 is also met. You can rephrase that as the following condition:

pos_qu==1 or k[pos_qu+1:]==selection[pos_qu+1:]

Putting this into your comprehension:

bigrams=[(k,v) for (k,v) in dict_bigrams.items()
         if k[:pos_qu]==selection[:pos_qu]
         and (pos_qu==1 or k[pos_qu+1:]==selection[pos_qu+1:])
         and k[pos_qu] not in alphabet.values()]

Upvotes: 2

Joran Beasley
Joran Beasley

Reputation: 113930

just change the order

bigrams=[(k,v) for (k,v) in dict_bigrams.items()
     if k[:pos_qu]==selection[:pos_qu] #evaluated first
     and  pos_qu!=1 #if true continue and evaluate this next
     and (k[pos_qu+1:]==selection[pos_qu+1:]) #if pos_qu != 1 lastly eval this

as the comment mentions this is not a very pythonic list comprehension and would be much more readable as a standard for loop..

Upvotes: 1

Related Questions