views:

4645

answers:

10

Hello,

Sometimes I break long conditions in IFs to several lines. The most obvious way to do this is:

  if (cond1 == 'val1' and cond2 == 'val2' and
      cond3 == 'val3' and cond4 == 'val4'):
      do_something

Isn't very very appealing visually, because the action blends with the conditions. However, it is the natural way using correct Python indentation of 4 spaces.

Edit:

By the way, for the moment I'm using:

  if (    cond1 == 'val1' and cond2 == 'val2' and
          cond3 == 'val3' and cond4 == 'val4'):
      do_something

Not very pretty, I know :-)

Can you recommend an alternative way ?

+1  A: 

This doesn't improve so much but...

allCondsAreOK = (cond1 == 'val1' and cond2 == 'val2' and
                 cond3 == 'val3' and cond4 == 'val4')

if allCondsAreOK:
   do_something
Federico Ramponi
Interesting alternative. But 2 extra lines :-)
Eli Bendersky
Wouldnt really work that well in an iterative loop, wouldnt work with functions doing something... and to be fair - ugly
Mez
If you're only going to use a variable once, I'd prefer not to use a variable at all.
Brian
brian, I partly disagree. Using variables for intermediate results of a calculation can make code easier to understand, and in a compiled language won't have any performance impact. It probably would do in python, though I wouldn't use python at all if perfomance was that important.
Mark Baker
+13  A: 

You don't need to use 4 spaces on your second conditional line. Maybe use:

if (cond1 == 'val1' and cond2 == 'val2' and 
       cond3 == 'val3' and cond4 == 'val4'):
    do_something

Also, don't forget the whitespace is more flexible than you might think:

if (   
       cond1 == 'val1' and cond2 == 'val2' and 
       cond3 == 'val3' and cond4 == 'val4'
   ):
    do_something
if    (cond1 == 'val1' and cond2 == 'val2' and 
       cond3 == 'val3' and cond4 == 'val4'):
    do_something

Both of those are fairly ugly though.

Maybe lose the brackets?

if cond1 == 'val1' and cond2 == 'val2' and \
   cond3 == 'val3' and cond4 == 'val4':
    do_something

This at least gives you some differentiation.

Or even:

if cond1 == 'val1' and cond2 == 'val2' and \
                       cond3 == 'val3' and \
                       cond4 == 'val4':
    do_something

I think I prefer:

if cond1 == 'val1' and \
   cond2 == 'val2' and \
   cond3 == 'val3' and \
   cond4 == 'val4':
    do_something

Here's the Style Guide, but it doesn't recommend doing anything special in this case.

Harley
Thanks, an interesting overview of alternatives
Eli Bendersky
+5  A: 

I suggest moving the and keyword to the second line and indenting all lines containing conditions with two spaces instead of four:

if (cond1 == 'val1' and cond2 == 'val2'
  and cond3 == 'val3' and cond4 == 'val4'):
    do_something

This is exactly how I solve this problem in my code. Having a keyword as the first word in the line makes the condition a lot more readable, and reducing the number of spaces further distinguishes condition from action.

DzinX
I read somewhere in either Gries or Djikstra that putting the logic operator at the front of the line -- making more visible -- helped. And I've been doing that since the 90's. And it helps.
S.Lott
Note that the Style Guide recommends putting the conditional at the end of the line.
Harley
That's true, although I never agreed with it on this. It's only a guide, after all.
DzinX
+1  A: 

I prefer this style when I have a terribly large if:

if (
  expr1
  and (expr2 or expr3)
  and hasattr(thingy1, '__eq__')
  or status=="HappyTimes"
):
  do_stuff()
else:
  do_other_stuff()
Deestan
Not very proof-read, apparently. :)
Deestan
+6  A: 

I've resorted to the following in the degenerate case where it's simply AND's or OR's.

if all( [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4'] ):

if any( [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4'] ):

It shaves a few characters and makes it clear that there's no subtlety to the condition.

S.Lott
This is an interesting approach. Doesn't address the issue of long conditions though
Eli Bendersky
It's ok if you don't care about shortcircuiting.
Constantin
@Constantin: The point was extensible, not fast. For fast, this isn't ideal.
S.Lott
shortcirtuiting is not always about fast. While not good coding practice, you may have existing code like this: `if destroy_world and DestroyTheWorld() == world_is_destroyed: ...`. Great, now you just destroyed the world on accident. HOW COULD YOU?
Aaron
+3  A: 

"all" and "any" are nice for the many conditions of same type case. BUT they always evaluates all conditions. As shown in this example:

def c1():
    print " Executed c1"
    return False
def c2():
    print " Executed c2"
    return False


print "simple and (aborts early!)"
if c1() and c2():
    pass

print

print "all (executes all :( )"
if all((c1(),c2())):
    pass

print
Anders Waldenborg
Incorrect! They only do because *you* do. Try all(f() for f in [c1, c2]).
Aaron Gallagher
+3  A: 

Someone has to champion use of vertical whitespace here! :)

if (     cond1 == val1
     and cond2 == val2
     and cond3 == val3
   ):
    do_stuff()

This makes each condition clearly visible. It also allows cleaner expression of more complex conditions:

if (    cond1 == val1
     or 
        (     cond2_1 == val2_1
          and cond2_2 >= val2_2
          and cond2_3 != bad2_3
        )
   ):
    do_more_stuff()

Yes, we're trading off a bit of vertical real estate for clarity. Well worth it IMO.

Kevin Little
+1  A: 

Just a few other random ideas for completeness's sake. If they work for you, use them. Otherwise, you're probably better off trying something else.

You could also do this with a dictionary:

>>> x = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> y = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> x == y
True

This option is more complicated, but you may also find it useful:

class Klass(object):
    def __init__(self, some_vars):
        #initialize conditions here
    def __nonzero__(self):
        return (self.cond1 == 'val1' and self.cond2 == 'val2' and
                self.cond3 == 'val3' and self.cond4 == 'val4')

foo = Klass()
if foo:
    print "foo is true!"
else:
    print "foo is false!"

Dunno if that works for you, but it's another option to consider. Here's one more way:

class Klass(object):
    def __init__(self):
        #initialize conditions here
    def __eq__(self):
        return (self.cond1 == 'val1' and self.cond2 == 'val2' and
               self.cond3 == 'val3' and self.cond4 == 'val4')

x = Klass(some_values)
y = Klass(some_other_values)
if x == y:
    print 'x == y'
else:
    print 'x!=y'

The last two I haven't tested, but the concepts should be enough to get you going if that's what you want to go with.

(And for the record, if this is just a one time thing, you're probably just better off using the method you presented at first. If you're doing the comparison in lots of places, these methods may enhance readability enough to make you not feel so bad about the fact that they are kind of hacky.)

Jason Baker
A: 

What if we only insert an additional blank line between the condition and the body and do the rest in the canonical way?

if (cond1 == 'val1' and cond2 == 'val2' and
    cond3 == 'val3' and cond4 == 'val4'):

    do_something

p.s. I always use tabs, not spaces; I cannot fine-tune...

Federico Ramponi
This would be very confusing, especially when the body of the conditional is long, I think.
Eli Bendersky
A: 

Pack your conditions into a list, then do smth. like:

if False not in Conditions:
    do_something
psihodelia