Whenever I'm coding something that requires a lot of conditionals, I end up doing this:
if foo:
if bar:
if foobar:
if barfoo:
if foobarfoo:
if barfoobar:
# And forever and ever and ever
I can't write if foo and bar and foobar and ... because I check for the value list elements (if foo[1] == 'bar') inside of an if somewhere down the line, and if the list index don't exist, I get an error.
Is there a shortcut to conditionally checking things like this, or an alternative method? Thanks.
7 Answers 7
I can't write if foo and bar and foobar and ... because I call list elements inside of an if somewhere down the line, and if the list index don't exist, I get an error.
in python,and short circuits. If the left side of the expression is false, the right side is not evaluated at all.
foo = dict()
if 'bar' in foo and foo['bar']:
doSomething()
3 Comments
Fail fast:
if not foo:
return
if not foobar:
return
and so forth.
Comments
There's also
if all((foo, bar, foobar, barfoo, foobarfoo, barfoobar)):
print "oh yeah"
all will also shortcircuit
2 Comments
Forgive me if I'm stating the obvious -- but if you're checking for many different conditions in advance of one or two operations, you might be better off using try/except -- especially for those conditions (if any) that are clear error conditions.
1 Comment
try:except: is fast, unless you actually hit an exception.See if you can't break some of that out into a function that includes some of the conditionals (assuming some are in common with your various cases).
1 Comment
Break it up into several sub-components where appropriate. As for where to draw the dividing lines, that's really up to you. While a huge staircase of conditional statements isn't great, neither is a massive if-statement with so many predicates that they wrap several lines. Instead, try to group your conditions into logical bunches.
You might write it as:
if foo and bar and foobar:
...
if barfoo and foobarfoo and barfoobar:
...
I also suggest introducing helper methods along the way. Even if those helper methods are called only from this code, that's fine.
def handle_bar():
if barfoo and foobarfoo and barfoobar:
...
if foo and bar and foobar:
...
handle_bar()
If scopes get confusing or you find yourself passing around too much state as function arguments, wrap it in a class and use member variables where its conceptually appropriate.
Overall, my advice is to separate concepts into individual pieces of code at an appropriate granularity. If you don't do it at all, you get a long piece of code that requires lots of scrolling to see the big picture. If you over-do it, you force the reader to jump around your code too much.
Comments
If you have more than 3 to 5 tests (or more), consider keeping your conditions in a dictionary, list or tuple. Then test that data structure. Much cleaner than many individual named data.
If you are testing "truth" against a named list of variables of unknown length or a sequence data structure (like a list or tuple) you can do this:
def all_true(*args):
for test in args:
if bool(test) is False: return False
return True
foo=bar=foobar=barfoo=foobarfoo=barfoobar=1
if foo:
if bar:
if foobar:
if barfoo:
if foobarfoo:
if barfoobar:
print "True by Stairs!"
if all_true(foo,bar,foobar,barfoo,foobarfoo,barfoobar):
print "True by function!"
t=(foo,bar,foobar,barfoo,foobarfoo,barfoobar)
if all_true(*t): print "The tuple is true!"
l=[foo,bar,foobar,barfoo,foobarfoo,barfoobar]
if all_true(*l): print "list is true!"
bar=0
# run the same tests...
The all_true() function will short-circuit against the first false it finds.
if?"and/orare short-circuited.